Extending Blockly with new openHAB commands

Refactoring of Script Parameter Handling

Even though you mentioned the code you gave me wasn’t tested at all it actually worked pretty well. :slight_smile: Though my code also generated a working result, yours is more elegant. It now produces the following code

var logger = Java.type('org.slf4j.LoggerFactory').getLogger('org.openhab.rule.' + ctx.ruleUID);

function addFrameworkService (serviceClass) {
  var bundleContext = Java.type('org.osgi.framework.FrameworkUtil').getBundle(scriptExtension.class).getBundleContext();
  var serviceReference = bundleContext.getServiceReference(serviceClass);
  return bundleContext.getService(serviceReference);
}

var ruleManager = addFrameworkService('org.openhab.core.automation.RuleManager');

function convertDictionaryToHashMap (dict) {
  if (!dict || dict.length === 0) return null;
  var map = new java.util.HashMap();
  Object.keys(dict).forEach(function (key) {
    map.put(key, dict[key]);
  });
  return map;
}


logger.error('abc');
ruleManager.runNow('helloWorld', true, convertDictionaryToHashMap({'jonas': '14', 'stefan': '43'}));

This approach also allowed me to make the ephemeris code more elegant for the conversion of ZoneDatetimes…

  1. Persistence Blocks I am also done with all persistence blocks which I separated a bit as mentioned above and now they look fine

and

Question: Do we really need item.persist() ? I am wondering why I would call that explicitly instead of setting a state (which in turn would be persisted)

  1. regarding the new PR: what is the best way to provide updates with git without generating too many commits?

just do a rebase after the commit like mentioned above ?

Finally

So basically I am done now with the next part of the blocklies and I would wait until the review of the PR to start with what is left and the new request from Rich above

Yes. Consider the following. Let’s say I have a sensor which periodically throws an unreasonable value. Let’s say a thermometer which sometimes reports 100 °C for a living space. That value is unreasonable. If that value is persisted then the charts will be all messed up, averageSince would not be correct and so on. So I want to filter out those erroneous values.

So I exclude that Item from being saved on everyChange or everyUpdate and I create a Rule that checks the value to make sure it’s reasonable before I persist it.

In short, any time one has a situation where they want to perform some logic before deciding whether or not to persist a value one would use that action in a rule.

You are pretty creative on those things - I wouldn’t have come up with that issue but that totally makes sense to me. I will do my very best (shouldn’t be too much work anyway)

Oh, I didn’t have to come up with the issue. I’ve had to help more than one user on the forum solve exactly that problem. It’s not creativity; it’s experience. :wink:

1 Like

Trying to give feedback a little earlier this time :wink:

What happens if one selects ‘previous’? The date selection doesn’t make sense in that case. Should that one have its own block?

How does that dictionary block work? Does the ‘settings’ button allow choosing the dictionary keys, which then provides a ‘docking’ space for each value? I wonder if it would make sense to also have ‘docking’ spaces for the map keys. Also small wording suggestion: add ‘create’ to the title (‘create dictionary of’).
Additionally, I wonder whether the parameter dock should be named ('Call script […] with parameters […]), because I’m not sure people will understand the meaning of that dock.

What happens if one selects ‘previous’? The date selection doesn’t make sense in that case. Should that one have its own block?

You are right, I only added that very late and overlooked that. Really a pity. I “could” use a “mutator” there which would then omit that part when using the “previous” but I have never done that. A single block though is not really nice. I need to think about that.

Does the ‘settings’ button allow choosing the dictionary keys,…

It does allow to add empty one and you can then enter the key and attach a value. People using “scripts” are rathe power users and we also are writing docs.

Call script […] with parameters

not sure here because you could actually call it without parameter by not adding a dictionary …

Yannick, Rich, what do you think?

I tend to agree. That doc seems to imply that something is required there to be complete. It would be more intuitive what’s going on here with a more explicit name like that.

Please don’t lose sight of this, folks.
It pops up with regularity for “my second or third rule evvah does not work, help” - exactly your target audience.
It’s inescapable for OH3, where the next thing you want to control after lights is temperature, just about every off the shelf sensor and weather report will now be Number:Temperature quantity.

Dirty, dirty. I really think its worth effort to do this properly, to harp on temperature again you will be setting up user disappointments with °C/°F chaos.
The underlying framework sorts out valid operations between quantities; I’d say the key enabler here is somehow allowing users to enter a quantity constant. The most common application is surely of the form “is myTemperatureItem.state > 10 °C”

Maybe the user can choose a quantity “class” - regular number, temperature, energy, blah - from a list (though that’s quite long these days) and then choose the unit from the offered recognised units for that type.

I don’t understand exactly what you except as a block. Can you give a detailed example and what kind of a block and behavior you would expect?

If that was aimed at me, the simplest ‘quantity’ task is not I think any new block but added capability to existing.

Reproduce functionality of

if (myItem.state > 10 °C ) do xxx

which will require users to enter “10 °C” somehow.
And the execution code behind the scenes to deal with quantity Items and maths.

That’s it, that would cover more than half of the average users encounters with quantities.

There’s a lot of behind the scenes issues too - persistence functions will return Quantity values etc. which the underlying code has to deal with but won’t concern the blockly user.

Yes, I was asking you, thanks :slight_smile:

Understood, is that basically getting rid of metrics under the hood (=in the code) before comparing anything? What is the right algorithm to deal with that?

Not my decision, do what you think right.

In my view that would be a dirty kludge though :wink:
In the other rule systems, we can perform such as
if [Item state 20°F] > [constant 10 °C]
and it just works - “false” in this case, 20°F is colder than 10°C.

And similar maths
new var = [item state 20 feet] + [constant 15mm]
new var will be a quantity itself, 20.027 feet or whatever
Maths can involve both units and non-units
new var = [item state 20 feet] * 2 result quantity 40 feet
*new var = [item state 20 feet] / [ constant 2 feet] * result non-quantity 10
The framework takes care of behind the scenes conversions etc.
And of course we can postupdate or command Items with quantities e,g. thermostat setpoint. (That seems to be a good reason on its own not to just avoid this issue by discarding units)

As I understand it, this is pretty much automated because the “magic” is attached to the Quantity type Java object.
In other rule systems, we must take special action to create a Quantity when typing in “10°C” in order to get this stuff to work properly, e.g. new QuantityType("10 °C")
I guess in the point-n-click UI world, this would be as simple as detecting non-numeric input and making it a Quantity instead of a Number

I may be misleading you here, but I’d guess you would need to code to use Quantity type objects as an alternative to ‘ordinary’ number types you get from constants, states, commands, persistence returns etc.

There are some moves afoot that might impact this discussion.

  1. There is a push to make the JSScripting add-on be the “default” non-rules DSL language. Nashorn, the current JavaScript implementation is deprecated and will be gone when OH moves beyond Java 11 in a year or two. Yannick is already working to make the JavaScript code produced by Blockly compatible with JScripting (for the most part the changes are minor I think.

  2. One of the things that makes coding in non-Rules DSL languages so hard to use is the interface with OH is rather raw and riddled with type gotchas, even more so than Rules DSL. Therefore they all end up implementing a Helper Library. There is an active PR to include the Helper Library for JSScripting as part of the add-on. That means we don’t have to do any “clone from git and copy these files over there” type nonsense.

These are relevant to this discussion because sometime soon we might be in a position to add a good deal of the handling of quantity types and the like to the Helper Library that ships with the JSScripting addon which can then be leveraged by Blockly. So all that would need to be done in Blockly is to somehow allow the user to specify the units on a number block (or maybe there needs to be a new block) that converts to new QuantityType("<number> <unit>") like @rossko57 demonstrated. It will still be up to the user to keep track of when they are using the state of a Number with units or not and choose the right block but I think it will go a long way towards helping ease this pain faced by lots of rules developers.

Unfortunately, the list of units is frequently growing and there are combinations of units that are allowed with prefixes (e.g. k for kilo, m for mega, etc.). And there is no API to pull in the current list of supported units. Given that I don’t think even the docs manage to keep up with the list (note that the units are actually defined by an upstream third party library, not us I think) I would, at least for now, just make the units field of the quantity type block take a free text string to let the user enter arbitrary values for the units.

1 Like

The upstream library provides most of it but openhab core also defines missing units.

You little maniac! :wink: That has really cost me quite some headache because documentation is pretty rare about how to do that. I had to do a lot of trial and error but finally I even found a pretty simple solution. Now it looks like this

When choosing everything but “previous” the block looks like this:

and when choosing “previous” the block has changed to

so we only need one block. Hope you like that.

I do … this looks great! :+1:

A :beers: or l :+1: is appreciated :wink:

1 Like

I am pretty excited of this development, really makes everything less error prone, perhaps even for power users like me…

One small comment: should we use the term “item state” instead of “item value”. To keep the terminology consistent.

1 Like

Let me ask my co product owner :relaxed: Rich, what do you think?

1 Like

I always vote for consistency where ever possible. Now that it’s been brought to our attention I would recommend the use of “item state” too. That way it’s consistent with the concepts docs and all the millions of posting that refer to Item states.