Getting Started: Rules - Blockly

I’m returning to the Getting Started Tutorial. This is a first draft before I submit it to the official docs to give people a chance to review and make changes. It’s a wiki so please edit as necessary and leave a comment.

Thanks.


What should one do if the simple “when-then-but only if” rules are not enough? This is where the Script Actions and Script Conditions come into play. Both of these are a way for you to write code that can be as long and complex as it needs to be to achieve your goals.

As you recall from the previous page, one of the four types of conditions are Script Conditions and one of the four types of actions are Script Actions. Choosing either of those presents you with a choice of language to use. By default there are only three choices: Blockly, ECMAScript, and Rules DSL. If you’ve installed other automation add-ons, those might be listed as well.

For this part of the tutorial we will choose “Design with Blockly”.

Introduction to Blockly

Blockly is a visual coding language consisting of blocks that fit together. For people who prefer visual over text or those who are new to programming, Blockly can be a good choice. It even allows you to learn how write your own JavaScript by looking at the code that Blockly generates in the background and this code could be even copy and pasted into a pure ECMAScript rule as a starting point. If you have been around kids you may be familiar with Scratch. This is a similar environment customized for writing openHAB scripts.

A typical Blockly script might look something like this:

Before continuing, see if you can figure out what that does. This shouldn’t be too hard because one of Blockly’s intents is to provide visual code that is easy to comprehend.

Blocks Categories

Statements in Blockly are defined using blocks. Compatible blocks can attach to each other to build up your program. Incompatible blocks will not fit together making it harder to come up with impossible logic. The code also reads a little bit more like natural language.

There are a number of categories of blocks which you will find along the left hand side. All but the openHAB and Library categories are generic programming concepts and are where you will find the blocks to create variables, loop through lists, comparisons and conditionals, etc.

The openHAB category is where all the blocks that interact with openHAB are listed. These are where you’ll access Items, call actions, get data from persistence, create Timers (a way to schedule some code to run in the future), etc.

The Library category is where Blockly Libraries installed from the Marketplace will appear (see below and the previous discussion on Rule Templates).

This tutorial is not going to be a full tutorial on how to write code in Blockly. See Blockly Reference for a detailed tutorial and reference for doing that. [TODO change link to official docs when they get added]. Instead it’s a higher level example of building a rule step-by-step using Blockly.

Some sections will be short. If you are missing details, see the previous section for details as most of the steps are the same for creating all rules in the UI.

Building a Rule

For this example, we will build a rule that turns on a light when a motion sensor triggers and leaves it on until three minutes after the last motion is detected. But it will only do so from sundown to 23:00 and not do anything the rest of the day.

When: Triggers

The event that we care about in this case is when the motion sensor Item indicates that it has detected motion. How this is represented can vary from one device to another. For example, one might use a Switch Item and send an ON command to that Item every time motion is detected. Others might use a Contact Item and update it to OPEN when motion is detected. Still others might use an Event Channel to trigger a rule without an Item at all. Some motion sensors will keep the Item ON while motion is detected and then set it to OFF, others will only send it to ON and the Item never returns to OFF.

There can be differences in behavior too. For example some will have a timeout where they won’t report newly detected motion for a configured amount of time has passed. You must understand how your motion detector works to choose an appropriate trigger and write an appropriate action. For example, if the motion sensor has a timeout of five minutes (i.e. it only reports motion every five minutes), the timer in your action for the rule must be five minutes or longer. Shorter and the rule will turn off the light even when there is motion.

See the previous page for details on how to create the trigger you need. In this example we will use an ON command to the MotionSensor Item as the signal that there is motion. The motion sensor will timeout for 30 seconds before reporting motion again. There are three options to choose from which are explain very well here. In simple terms “changed” means the rule only triggers if the state has changed for example from OFF to ON while “was updated” would even trigger the rule if “OFF” was set to an item state that was already OFF.

Then: Actions

Now let’s work on what we want to have happen when the rule triggers. As mentioned above we want to turn the light on and leave it on until three minutes after the last motion is detected.

First create a new Action and choose Script Action and Design with Blockly.

To start, we want to be able to see that this rule is running. So we will first add a log statement which you will find in the openHAB → Logging & Output section. We will choose “info” for the level and type a message we want to see in openhab.log when this action is executed.

image

Next we want to command a light to turn ON. Under openHAB → Items & Things you’ll find a “send command” block. Drag that over and dock it under the log statement. We want to send ON to the Porch Light Item. If the light is already ON nothing will happen but, if your device behaves differently you might need to check to see if the light is already ON before sending the command.

Next we want to set a timer. A timer is a way to schedule some code to run later on. The rule will continue and exist after the timer is created (i.e. it doesn’t wait around) and the timer will execute in the background at the scheduled time.

In this case we want to wait three minutes. But we want to wait for three minutes after the last motion. This rule gets triggered every time motion is detected so we need to reschedule the timer every time motion is detected. A reschedule will tell the timer to run at a different time. Only when there has been three minutes of no motion will the blocks inside the Timer block be executed.

Under openHAB → Timers & Delays drag the “after” block that includes “reschedule” at the bottom. We need to change the time to three minutes. Notice the little triangle next to “seconds”. Clicking on that will bring up a list of options to choose from. Anywhere you see that triangle there are options (e.g. clicking on “info” for the log can change the logging level, clicking on “send command” lets you change to “post update”).

Give the Timer a meaningful name. It’s good to be in the habit of naming everything with a meaningful human readable name.

Now we need to add the block to run three minutes after the last motion was detected. We want to send an OFF command to the light. But, for testing and debugging we also want to log something out so add a log statement too.

Trigger the rule a few times and watch the logs to make sure you see the commands and logging events as expected.

But only if: Conditions

We have the when and the then so now we need the “But only if” to limit when the rule can run to between sundown and 23:00. Add a condition and choose a Script Condition and select Deign with Blockly as the language.

A script condition is just like an action except the last statement execute must evaluate to a boolean (true or false). When it’s true the actions will execute. When false it won’t.

So we need to have a way to compare the current time with a hard coded time (23:00) and a time held by an Item linked to one of the Astro binding’s sunset channels.

Looking around the available blocks will show us that there are no such blocks built in (as of this writing). The first thing to do in this situation is to check the Marketplace to see if there is a Blockly library that does what we need. Indeed there is one for comparing times!

Install a Library

Navigate to Settings → Automation and scroll down to “Block Libraries”. Browse around to see if there are relevant libraries, in this case the “Date and Time” one looks most promising.

Click on it and add it and return to your rule. Now you will find that there is a dateTime entry under “Libraries”. It looks like we will be using the “now” (the current time), “ZDT from item” (sunset time from the Astro binding), the ZDT with date" (hard coded time), and the “is” block to make the comparisons.

Build the Condition

Now that we have the blocks we need, let’s build the Condition. First notice that we have the same operation that needs to be executed twice. Often, when one encounters this situation, a function is a way to save duplicated effort. Let’s try that here.

Under “Functions” drag the “to do something” block with the return statement. Next to “to” give it a meaningful name, we’ll use “compareDTs”. In this case, in order to do something useful we need to pass arguments to the function, two date times. To do this click the cog icon. This will bring up a way to add or remove features to any block that has the cog. In this case we want to add two “inputs”. Drag them from the left to the right and give them names. You will now see the two inputs listed on the block.

Now we want to compare the two passed in date times and return a value to indicate whether time1 is before or after time2. We will use 1 to indicate that time2 is after time1 and -1 to indicate that time2 is before time1. So first we need to create a variable so under Variables create a new variable called “tcomp” for “time compare”. Then drag “set tcompt to” and dock it inside the function. Go to Math and drag the “123” block to plug into the “set” block. Change the value to “0”.

image

Now we will add an if statement to compare the times. We have two conditions to we will use the cog to add an “else if” to the if statement to. You can find the “if” block under “Logic”.

Now we will use the new “is” block from the library to see if time1 is before time2 and set tcomp to 1. Drag the 'is" block and plug it into the first if socket. Then under “Variables” drag the “time1” variable and plug it into the first slot after “is”. Drag time2 to the the slot after “before”. We only care about the time so change the config after “use” to “time” and keep the resolution at “seconds”.

In this case we want to return 1 so right click on the “set tcomp” to block and choose “duplicate”. This will create a copy of that set of blocks. Set the value to to 1. Drag the block next to the first “do”.

Duplicate the “is” and “set” blocks again to populate the “else if”, this time setting to -1 when time1 is after time2.

Finally, drag the “tcomp” variable and plug it into the “return” socket.

The hard work is now done. Now we just need to create a statement when now is between sunset and 23:00. To keep the blocks from getting too long and complicated, let’s create some variables: “start” for the sunset time and “end” for 23:00. Set “start” to the “ZDT from item”, selecting the Sunset Item. Set “end” to the "“ZDT with date” block. Set the time to “23:00:00” (we don’t care what the date is set to).

Notice how the statements are not connected to our function. They represent a completely separate block of code.

Now all we need is the condition that evaluates to true or false when now is between start and end. From “Logic” drag the block that has “and” in it to just below our setting of the end variable. Normally a condition like this would dock to an if statement but we are going to use it on it’s own.

In the first slot, from the “Logic” category drag the block with “=”.

image

From “Functions” drag the block named “compareDTs” to the first open slot. Plug in “start” from variables for “time1” and “now” from the dateTime library for “time2”. From Math plug in “1” in the slot after the “=”.

Click under the “=” to select the whole comparison and duplicate it. Plug the duplicate into the slot after the “and” and change “time1” to “now” and “time2” to “end” and the number to “-1”.

That’s all there is to it.

Notes:

  • The function probably doesn’t add much here and it’s use is somewhat contrived to demonstrate how to create and use one. However, it does keep there from being too much all in one set of blocks which can become challenging to read and understand.

  • What if the end time is after midnight? Edge cases like that happen all the time and it will make your code much more complicated. But don’t be afraid to deal with them. Take is slow and gradually build up to them.

  • That script condition could use some logging too. What do you think it makes sense to log out?

Debugging and Troubleshooting

No one writes perfect code all the time the first time. So what is one to do when it doesn’t work as expected?

  • Add logging and make sure to log out meaningful information such as the values of variables. Look in the “Text” category for operations to combine strings together for logging.

  • Watch the logs for errors, your log statements, and relevant events (see the introduction in the blockly reference on how to easily access log information)
    TODO: replace final document url when moved

  • When asking for help on the forum, don’t just post a screenshot of the blocks. Also post the code the blocks generate or, even better back out of the script to the rule and post the contents of the “Code” tab which gives the helpers on the forum the full picture.

Note that you can even add comments to your blocks to document the intention like so


which is even reflected in the generated code. Right click on a block and choose “add comment”. Comments are a great way to document reminders and give future you a hint on what you were thinking as you wrote this code.

  • Simplify when a rule doesn’t work and gradually rebuild it.

Tips and Tricks

  • Build the rule gradually, testing each step of the way.

  • Make heavy use of logging and watch the logs when testing.

  • Create functions rather than repeat functionality.

  • Functions can also be a way to break up big and complicated blocks into easier to manage and understand chunks.

  • Use right-click and duplicate to create copies of blocks that are close to what you want.

  • Look at the code that the blocks will render into to learn more about how the blocks work. Click on the blue file icon in the lower right corner image. You can also use the Ctrl-B keyboard shortcut to switch between the blocks and the code preview.

  • In a pinch, under the “Run and Process” category there is an “inline script” block (since 3.1). That will let you write your own custom few lines of ECMAScript 5.1 code. This is a great way to deal with missing features in the Blockly blocks or to do something more efficiently than can be done with the blocks.

Previous
Next

6 Likes

@stefan.hoehn, @Andrew_Rowe, and @ysc,

If you have a little time to spare I would love a quick review of this page in particular by the Blockly experts to make sure I’ve not said or done something wrong or bone headed in demonstrating Blockly. My example got way longer and more complex than I intended so I might switch to a different example (suggestions are encouraged).

Thanks!

I pm’d you. I will do as soon as I can.

I do appreciate your efforts in writing all of this. To be honest because of timing I have not read through everything but I have some concerns based on my current understanding:

If I read correct you want to compare current time with sunset and 11pm in your example condition.
Instead of converting a datetime to string, split the string into several parts and comparing these parts, shouldn’t we better compare different datetimes objects directly?
E.g. on the market place there is a nice library that will tell you if sunset has already passed or not.

If that’s not available ootb, I would recommend that we should maybe focus on building the right blocks instead of recommending some type conversions. I’m happy to help here.

In general: maybe instead of explaining such a complex example focus on the basics e g. What blocks to use when, how to modify blocks (e.g. How to change an if block into an if-else block) or give some other basics examples.

1 Like

Blockly doesn’t yet have blocks that support that. It’s on the list to be done, just not done yet.

There will always be some sorts of type conversions required. There always is. But creating “the right blocks” is already a big focus and a ton has already been done on this front.

The point of the tutorial is to show how to build a rule using Blockly via an example. I agreed that this example is a little more complex than I anticipated but what ever example is created needs to be:

  • singular, not a bunch of different separate examples, each page in Getting Started is an end-to-end example
  • demonstrate enough of how to work with Blockly that the reader can effectively use the reference docs
    • functions: creating and calling
    • using the cog icon and drop down lists
    • variables
    • working with lists
    • conditional logic statements
  • not simply regurgitate the reference docs and instead focus on the higher level “here is how you go about writing a script using Blockly”.

I agree that this example is too complex.

Unfortunately this means that tutorials are very long, which can turn many people off. I’m far more interested in the smaller, separate examples (just like the timer one at the top of this tutorial) because it’s easy to immediately digest, understand, and then start thinking about how it can be implemented for my situations.

I agree - I was under the assumption that Blockly was for newbies. Certainly, most of the Blockly queries (if they haven’t come from me!) have been about modifying blocks to perform the very basic tasks as it’s not obvious.

Perhaps, like with the general Getting Started guide, this should be split into Basic and Advanced tutorials? I’d be happy to contribute to the Basic one.

The bit of openHAB documentation that I found most useful, by a long margin, was the “But How Do I…?” section of the openHAB Helper Libraries documentation. Lots of super simple examples, getting a little more complex as you go on.

I think it’s really only slightly beyond what it needs to be. I would advocate just changing to the condition “between sunset and midnight”. That pares down the complexity just a little bit by removing the endParts creation and comparison as well as the less than comparisons in the datecomp function.

Also, if you keep this example in general, the screenshot after:

Now open the Math category and drag the “123” block…

Is identical to the one before it and didn’t capture the addition of the number block.

This may be true, but these pages must be able to accommodate the users starting from 0 experience at all. For something as complex as OH, there’s just no shortcut. Examples and tutorials get long when they have to work up from fundamentals and the only recourse we have there is to make them actually helpful and engaging and Rich has done a pretty good job of that.

I’ve been looking into Bacula lately to see if I want to move my backup strategies to that… Heavens! That’s several long tutorials (some of them read several times) before I even made my first test backup…again, there’s just no real shortcut if I want to understand and do it right.

No argument here, a page like that is of critical importance. But that’s for users that are at the level where they understand enough to know what question they need to ask. There are a few levels of beginner experience before that where the user needs to know what kind of concepts are available and how to even go about finding out more detailed information.

1 Like

We’ll agree to disagree again then.

This tutorial, as currently written, is not for those levels of beginner either. It starts by saying “There’s no easy way to do this thing…” and then deep dives into an advanced example. Hence my suggestion to split the Blockly topic into Basic and Advanced topics, where the Basic tutorial has a number of “Here’s how to do this easy thing…” examples.

Those are covered in the reference documents in spades for Blockly. It’s redundant and doesn’t serve any purpose to duplicate the reference docs here. You are more interested in the Timer example. Someone else will be more interested in calling executeCommandLine or calling Ephemeris or … By the time you cover them all, all you’ve done is rewritten the reference docs. That’s not what this is for.

In fact, the reference docs that @stefan.hoehn and @Andrew_Rowe put together already comes pretty close to the “But how do I…” from the JSR223 Helper Library already.

But again, the purpose of this tutorial is not to teach the end user everything they need to know to write Blockly code. That’s what the reference docs are for. It’s to teach the user how to go about constructing a rule using Blockly. Once it’s read and they’ve done it once or twice, they should never have to look at this tutorial again. It’s to bridge the gap between “I’ve pressed the + icon, now what?” and “how do I create a timer that reschedules and at the end calls a command line and sends a Telegram message.”

I strongly recommend everyone go read the reference docs to see what’s there. If it’s there, I don’t want to reproduce it here. And they really are quite good and complete and every bock is covered. What they don’t have though is an end-to-end walk through on what it’s like to code a script with it. That’s what this tutorial is for.

And I should elaborate a bit. The reason that I want to show those five features of Blockly is not to specifically show the user how to use those blocks, it’s to show the user how to think about their rule and how to break their requirements into discrete blocks (no pun intended). No reference guide is going to show that.

That could work. It’s probably cut out about a good 10-15% of the total. I’m still not super happy with using time comparisons for this though because at some point we will have proper time comparison blocks. My intent is to go back and change it to use those when that happens, but still maybe it’d be better to come up with some other set of conditions that are too complex to do with simple conditions but less complex than parsing the hours and minutes out of a date time string.

Thanks, I’ll generate a new one tomorrow. I had done the same thing in another place too but must have missed this one. It’s so easy to take the screenshot but forget to copy it before pasting.

Just try looking into NUT.

As an aside, one thing that is completely missing here is accessing the event Object to get information about what triggered the rule. Grrrr. Knowing that sort of things is there is super important.

It’s clear we disagree on what a “Getting started…” tutorial should contain. And that’s fine, especially as no one is getting paid!

Thanks for all the effort everyone is putting in!

Just to be clear though. What you are talking about, little example for how to do discrete things in Blockly is definitely needed. I think the only difference is in where that stuff belongs. Please do review the reference docs and make sure that there are enough and the right examples there.

Thank you very much @rlkoshak for doing this. I added a mention of the Ctrl-B shortcut in the OP.
It is indeed a very elaborate example that I think ultimately should be addressed with date & time manipulation blocks instead, but still offers a good preview of what you can achieve with Blockly.

I would perhaps add a mention that this Blockly-generated code you can copy and paste in a non-Blockly rule or script if you want to move away from Blockly and have more control - it is the ever-debated issue of whether Blockly can be considered a stepping stone to actual JS code or not (or merely a beginner-friendly rule builder and that’s that).

Thanks for the add and I’ll add the mention.

I am reconsidering the example as well. Once we have the time comparison blocks it might become too simple so I probably need to be prepared for a different example anyway. I just need to come up with what that should be. I want it to be a practical and common thing we all do.

Or maybe it makes sense to create a library of blocks that can be used to do the date time comparisons and the tutorial starts with installing that. Then almost all the complexity can be hidden from the tutorial and when the official blocks come along it’s not that big of an edit.

I’m still pondering and open to ideas from anyone. They just have to meet the criteria I outlined above to fit with the intent of the tutorial, to show how to go about building a rule with Blockly, not to show how to do everything one can do in Blockly.

What about instead of “light turns on only between X and Y time”, “light turns on only if room illumination is below Z”. That keeps most of what you’ve already done. Then, you can demonstrate how to convert it from a specific rule to a general rule by building a dictionary of different room illumination levels with the motion item names as the keys (an important concept) and show how check the event object for the name of the motion item to fetch the correct illumination level.

1 Like

I like it. I’ll give that a go later today.

Hi Rich,

as discussed with you I reviewed the docs and edited it directly in the wiki where you can easily diff the changes. Please take each change as proposed editing and revert it as much like and wherever you want.

In general, I like the tutorial and even the example you came up with. Especially the fact that you used some standard blocks that are mostly overlooked by many people. However, I would still feel that it would help to start and add some more basic example upfront as otherwise I could imagine that the learning curve would be a bit steep for most unexperienced people.

@ysc and Rich: As you know I had to step back for personal reasons for a few weeks but now the itch is growing to get the date comparison done soon :wink: See you on the “Extending Blockly page” soon :slight_smile:

1 Like

OK everyone. I reworked the example and it probably makes a bit more sense now. I use the dateTime library for the date time operations instead of parsing the date time strings. I still use a function so I can demonstrate how those work.

I know I misspelled “compareDTs” in the earlier screen shots. I’ll fix those when I move this to the official docs. But it’s a pretty significant rewrite of the middle part of the tutorial.

I probably took more screen shots than was strictly necessary but it’s easier to remove than to add. I also broke up the Action and showed how it gets built too rather than skipping it.

I hope I addressed the major concerns about the example shown and am open to further suggestions.

Thanks!

3 Likes

I like the simple example at the beginning, flowing into the advanced example later. Good work, and thanks for putting this effort in!

Looks much better and comprehensible now.
The only thing I am a bit unsure if downloading a library is 1) simple enough for a beginner and 2) something that a tutorial should depend on, though I am not really worried about it. Just my 2 cents.

Because there are a lot of thinks that will always need to be downloaded as a library in Blockly, such as access to the binding Actions (e.g. Telegram) I think it’s important to show that in this tutorial. And it’s no more complicated than installing an add-on, which was already covered in the tutorial way back on the second page. By the time the user gets to this page in the tutorial (remember, the tutorial builds and assumes the user already knows stuff presented in the earlier parts of the tutorial) they should know how to install an add-on. Above I present the stuff that’s slightly different from installing a binding.

tl;dr, I think it’s important to show installation of a Blockly library here. They are not going to get it anywhere else.

I too am a little hesitant to rely on a Blockly library in the tutorial as it raises the risk that if that library ever goes away the tutorial will have to be updated. But to show installation and use of a library, I have to use some library. Using this one let me stick to the same example which I think is a really good one and worth preserving if possible.