Extending Blockly with new openHAB commands

Announcement: New Blockly Workspace Zooming

I provided another enhancement for blockly: From now on, you can zoom the Blockly Workspace:

image

You can find this in the bottom right corner. This also allows pinching on touch screens or touch pads.

Not to conflict with the “old” Code-Button, this was “moved” into a new version into the footer of the workspace (Kudos to @ysc):

5 Likes

I have a generic Blockly script that runs when a light group is triggered by a motion sensor. It then waits a certain amount of time to turn off the light or in the case of a retrigger of the motion sensor keep the lights on. I’ve tried to do this with the timers available in the OpenHAB section but that doesn’t work properly as timers (or probably the event context) is lost when a different light group is triggered.

I’m now using the common blocks from this community marketplace post: Collection of common blocks. Basically the timer block that keeps the triggering event context. That block actually works great and allows me to create the desired automation.

Is it possible to include the common blocks (or at least the specific timer one that keeps the event context) into the main OpenHAB section? It’s to avoid that I’m dependent on an ‘external’ block instead of what is available in OpenHAB out of the box. Thanks for considering!

As @rlkoshak is our genie on timers I loop him in here:

Rich, the first block is the one that Marco mentions, the second is ours

image

The code difference is minimal

if (typeof this.timers['MyTimer'] === 'undefined' || this.timers['MyTimer'].hasTerminated()) {
  this.timers['MyTimer'] = scriptExecution.createTimerWithArgument(zdt.now().plusSeconds(10),event, function (event) {
  })
} else {
  this.timers['MyTimer'].reschedule(zdt.now().plusSeconds(10));
}

if (typeof this.timers['MyTimer'] === 'undefined' || this.timers['MyTimer'].hasTerminated()) {
  this.timers['MyTimer'] = scriptExecution.createTimer(zdt.now().plusSeconds(10), function () {
    })
} else {
  this.timers['MyTimer'].reschedule(zdt.now().plusSeconds(10));
}

The only difference is that it uses

  • createTimerWithArgument
  • add the event-variable as an additional parameter and forwards it to the function

Could we not just do that in general?
If not, we could give the timer-blocks an additional choice to choose from but I need to check of that would be backward compatible, so it doesn’t break old blocks.

What do you think?

There are subtitles involved here that probably needs to be explored before deciding how to proceed. The big concern is that

  1. sometimes you want to have the event and possibly other stuff as it was when the timer was created

  2. other times you want to have the event and other stuff as it is right now, possibly having been changed since the timer was created on a subsequent rule trigger.

The difference is subtle but important. Ideally both use cases should be supported. I personally prefer using function generators because it lets you “fix” multiple variables rather than just the one variable. For example:

var timerGenerator = (one, two) => {
  return function() {
    // function called by the timer which uses arguments one and two somehow
  }
}

...

scriptExecution.createTimer(zdt.now().plusSeconds(10), timerGenerator(event, foo));

We call a function passing the arguments and get back a function that will get called by the timer. The passed in arguments become fixed and won’t be overridden when the rule runs again. But that’s a pretty big lift for the typical blockly user (though I think it should be possible).

I think maybe splitting the difference would be be better. Rather than forcing the argument passed to the timer to be the event, why not let us pass our own argument, including event? But I think that would involve creating a new “timer with argument” block where one can select the event or any other variable defined.

The first Blockly video tutorial has been published on the 21st of June on the openHAB Video Channel.

The second Blockly video tutorial has been published on the 5 of July on the openHAB Video Channel.

See the announcement here for further information.

3 Likes

Hello All

Has anyone worked with location type items in Blockly? are there any speciality blocks that i could simply import?

I would like to be able to calculate the distance to a moving object (a car) and get a message from openHAB when the object (car) is closer to home than a certain distance.

I created this one for this usecase: Geographic information system - #5 by timl But the “location type” itself is not supported yet. I worked with native float (latitude and longitude).

Regards,
Tim

cool i will check it out

Hello Stefan

this is the example of trying to use the “state” of a location item, as a string, to split off the latitude and longitude for further use.

edit; i guess the reason for not being able to treat the state of the location item as a string is that it is returned as an object

Hey @joriskofman
I also love blockly… here a much smaller code. You can create a list from the location item and access directly the first (0) and second (1) item:

Regards,
Tim

1 Like

Good one @timl your code also works if there would be an elevation field in the location item, mine would break

I finally found the time to implement it.

There is a pull request to be merged:

If everything goes well, it will be available in the next release.

These are the blocks that will be available soon:

oh_text_replace: a new text replace block that is missing in the standard text block section

oh_taggeditems: retrieve a list of items identified by one or multiple tags

image

oh_list_concatenate: allows to concatenate two lists into a new lists

oh_get_persistvalue: add new previousState (new mutating block)

image

oh_zdt_fromText: add time support

image

7 Likes

I added some more functionality to allow skipping the same state until it finds a different one to the current state:

This for example allows to find out how long a door has been kept open and if it exceeds a certain time, a notification is sent to the user to close it

You can even go further and use the tag block to gather ALL windows that are tagged with OpenState by running this rule once a minute:

Note: to be able to use the skip=true, rrd4j isn’t sufficient as it does not support skipping. You may add influxdb for that purpose and set the default persistence to that.

2 Likes

@ysc @rlkoshak

I’d like to have your opinion on the following topic.
Since we have introduced our blocks it more and more became clear to me that the naming of the word “item” in the blocks is actually not really consistent and it can lead to the wrong usage of the blocks

note that I just yesterday implemented some more rigid type checking on these which was actually flawed over the past months but still type checking can be “circumvented” when introducing variables (type checking is then not applied).

So what is the problem - let’s have a look at these blocks:

This here is actually not an item but only an item name that is picked by the user - it is essentially a string

image

This will return an item object

image

This doesn’t send a command to an item but to an item name to be precise

Again, in 4.0 I have implemented checks, that make sure that the blocks cannot be attached to the wrong place (that is still possible in 3.4) but with vars it can be pretty confusing and to be honest I myself fell intro that trap:

Looks right, doesn’t it?

Unfortunately it isn’t and it crashes. Let me show why:

“get state of item” expects an item-name and not an item which the variable actually contains.

Here are proposals

  1. It isn’t that big of an issue and we just have to document it well enough
  2. We rename the green item to become more precise

image

which then would become the following for all the other blocks (which might be a bit lengthy)

  1. We could try to detect in jsscripting (graalvm) if a name or an item object is given and therefore become more forgiving and resilient. However, that doesn’t improve the current implementation based on Nashorn.

So what’s your vote or do we have an option 4?

@ysc @rlkoshak

I‘d also like to have your opinion on a Blockly topic.
During the portation of Blockly to JS Scripting, @stefan.hoehn and I found out, that the Date & Time parsing Blocks for Nashorn generate ZonedDateTimes in the UTC time zone (indicated by a Z at the end of the ZonedDateTime‘s string representation).

In JS Scripting, the ISO parsing is using the system‘s time zone, e.g. for me Europe/Berlin, which is +01:00 from UTC.

We now have to discuss how to proceed: Should Blockly stay with using the UTC zone or use the system time zone?

IMO, we should use the system time zone, because:

  • openHAB DateTime Items also use the local system time zone (I found this out when I retrieved the state of one of my DateTime Items: REST API returned 2022-12- 27T19:59:16.696974+0100
  • Using UTC can lead to serious problems when comparing with DateTime Items, because they are in the system time zone:
    Imagine you want to check whether a DateTime Item is after 13:04. You create a Block, put 13:04 into it (you wouldn‘t think of: which zone am I, which Zone requires the Block, what do I have to input). Your Item has 13:05 as state, but because it is in your time zone +01:00, it is UTC 12:05. So you end up with a check, that says: 13:05 is not after 13:04, because you did not expect that they have different time zones. The check would only work if it was: 13:05 (+01:00) is after 12:04 (UTC).

IMO using UTC as time zone for user inputted times is a bug that breaks comparisons.
I propose to accept the system time zone that is used in JS Scripting‘s ISO parsing, even though the behaviour of some Blocks will be slightly different, e.g. when calculating the hours between a ZonedDateTime in winter and summer, because in Europe we have summer and winter time and the system time zone seems to respect that. But in that case as well, the system time zone IMO is the better solution, as it returns the correct value of hours between those two DateTimes in your zone.

  1. If even you fell into this trap I’m certain it will become a significant issue among others.

  2. I like this as the bare minimum thing to do. One of the strengths of Blockly is it’s self describing. Being explicit here helps with that.

But, what happens if you have the following?

get state of item -> get item -> item name [MyItem]

Does get state of item only support the Item name or can I also pass it the Item Object?

Could it make sense that we only have a get item and adjust the other blocks to always expect an Item Object?

Or the converse, always expect an Item name and the other blocks adjusted to convert to an Item Object?

I’m nervous about both ideas, especially when you start working with Group members.

And this begs the question, why do we have both get state of item and item name in addition to get x of item (which presumably can get both the state and Item name)? Is it because we get the name and state so often that we created a separate block for convenience or is there a deeper reason?

  1. Why would this be limited to graalvm? It seems possible to do with Nashorn too. I like this idea but it will make the underlying code a tiny bit more complicated.

  2. Ignoring the chaos and breaking changes, what if we did away with item name and instead we get the Item Object in that block? Adjust the other blocks to always want an Item Object instead of just the name. We’d need to keep the get item block for cases where an Item’s name is constructed in the code of course.

There’s a third option, change to use LocalDateTime. I’m not pushing that, just mentioning that it could be an option.

I would tend to agree. We need to use what ever gets stored in DateTimeTypes, which I believe uses the system time zone. If we are consistent with that we should have a smoother overall experience.

IMO this won‘t work great, because AFAIK openHAB internally heavily used ZonedDateTime and if we want to send the LocalDateTime to some openHAB API, we have to convert it to a ZonedDateTime and think of the time zone as well.
So I‘d prefer to use ZonedDateTime with the system time zone.

:+1: DateTimeType uses ZonedDateTime in it‘s constructor, and DateTimeType is used by the DateTime Items, which (at least when I checked that) use the system time zone (which obviously makes sense to have a time in your zone and not another one).

Before I start going through all what you are mentioning, I think the ONE thing I would bring up is that

we must not break anything.

This is even more important in blocks than it is with text languages especially when blocks return a type and we would change the output of that type. It will not only connect anymore but in some situation the whole rule would break and not even open anymore (I have experienced this myself often enough when I broke something during development)

Even changing the behaviour of a block is dangerous because Blockly-Users are (of course not necessarily) less experienced programmers than we are, so we need to make sure that the rules behave the same as before. The vast amount of people even don’t care about Nashorn or GraalVM (even though they may appreciate that with GraalVM they will get more).

So, without further ado, here are my comments

image

That was one thing that caused the trouble because you were able to stick to it but it lead to broken code that crashed. Since yesterday it is now typed. Get-State-of-Item requires an item-Object which itself requires an item-picker that returns the name.

Why did that happen?

The green item was on of there very first blocks that existed which does not return an item-object but just the name - it is the item name picker to be correct. That couldn’t be changed because it would have broken 80% of all rules that existed.

No, at least it doesn’t now. As I mentioned it broke the code. I could look into it if that was possible and I will put that on my list but still it doesn’t solve the name problem of the block. It is not clear if “item” on a blocks’s name refer to the item or only the name.

Most likely it will break many rules, so I very much doubt that. And just to bring that up before someone comes up with that idea :wink: : Migration of blocks to me is out of the question (at least as far my capacity of work is concerned unless someone steps up for it)

Again, similar to the proposal above, we cannot get rid of the other blocks as well because it would break rules that would is. I am not saying that we might be able to provide the possibility to stock a light item (aka the name) or a real item (object) into some other block and it is intelligent enough to know what it needs. I just don’t if we have enough test examples that we can make sure we cover all permutations. Let me put it that way, if someone would think through all examples we can do now with all blocks that stick together or use items with these combinations then I would start thinking about (don’t perceive this as a veto) designing because again, we must make 100% we do not break anything (even I have more 100 blockly rules which I don’t like to update, btw).

:100:

Yes, that was surely the reason. AFAIK, getState was one of the very first blocks that was provided (remember the times when we only had a handful of blocks :wink: ). And to be honest, it really makes sense. It is the item attribute that even I use in 90% of my rules.

I don’t mind doing backporting things to Nashorn (as long as it doesn’t break anything) and don’t worry too much about these two: the code that generates both ways are still pretty similar and it is still easy to maintain both which I am pretty happy about. So, no, that isn’t an issue.

As I said I would veto breaking anything because we would scare away our beloved blockly users right away.

I am happy to add or improve things and even provide some new features only to GraalVM but not break the old rules. For example the new rules for handling Units of Measurement will only be support with GraalVM and I am sure that is a good incentive to move over but the move is nothing but selecting the new script language and everything will be done in the background (note that moving back could break rules in case you use newer blocks)

Regarding the Time-Topic of @florian-h05

With you and me being there in the dev business for sometime we both know there is nothing worse in the world than enc%&&ding and T1mezones. So, my only take here is that new data blocks behave the same like old blocks when doing comparison and computing anything with them. To be honest, I wouldn’t guarantee that if we internally move to a new representation that this will result into a new unexpected behaviour.

I we still did this and decided to go for that, I vote for at least documenting that very that clearly when releasing the new version.

This is a great goal but also a recipe for technical debt and I’m not sure it’s completely achievable in the long run. At some point we will end up accreting so many legacy blocks that use of the language will suffer.

That’s why breaking changes are mostly isolated to major version updates to OH. Since we are now at such a transition between OH 3 and OH 4, if breaking changes are to be made, now’s the time to do it or else it’ll be two+ years before we get the chance again.

I’m not saying that this specific issue is important enough to have breaking changes. But I am saying now’s the time to consider them because once OH 4 is released, they will be much harder to make.

But at the end of the day, if we are not going to consider any change to clean things up for consistency sake, all we can do is properly label the blocks so it’s much clearer what they mean and do.

Perhaps it’s reasonable to deprecate some blocks and replace with new blocks in some cases or encourage the use of alternative already existing blocks. That should avoid breaking changes.

1 Like

Don’t get me wrong as mentioned above I am not totally against but …

breaking Blocks is targeting a different group of people than breaking script language users.

I’d be totally for it if we could migrate old blocks but I am afraid that this would be really tough (maybe some people join forces to find a way for that).

And to be honest on my side. I even had written blocks that needed refactoring for months but I was afraid of the work. The blocks that we are talking about are probably the most used blocks and would affect probably more than 50% of the blocks.

Deprecation is actually a good idea. Maybe it could be a strategy to tell people to change their blocks until autumn next year and then with M5 we can break “something” if we have kind of a preparation path…let’s think about that.
Maybe even if I could backport something that would allow both “worlds” as a mix (if the maintainers would allow it) on Nashorn (yes it is a bit of a pain to main…) in 3.4.x, so people could start using the new approach with the old approach and then delete the old block. Just thinking aloud loud.

To be fair, as a partner in crime I have asked you for your opinion, and I do appreciate it.

I like the idea that we could maybe make the blocks in question a bit more resilient or interchangeable (which i will definitely look into) and maybe we find some clever migration path.

At least I see there is no easy answer. :fist_right: :fist_left: