Skip to content

Commit 19d2da0

Browse files
committed
Add JRuby examples in rules_advanced.md
Signed-off-by: Jimmy Tanagra <jcode@tanagra.id.au>
1 parent 673da56 commit 19d2da0

File tree

2 files changed

+215
-11
lines changed

2 files changed

+215
-11
lines changed
48.8 KB
Loading

tutorials/getting_started/rules_advanced.md

Lines changed: 215 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,17 @@ For what ever reason, openHAB has you covered with text based Script Actions and
1616
openHAB supports a growing list of programming languages in which rules can be written.
1717
openHAB comes with the following languages to choose from:
1818

19-
| Language | Details | Intended Audience |
20-
|--------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------|
21-
| [Blockly]({{base}}/configuration/blockly/) | See the previous page | Non-developers |
22-
| [Rules DSL]({{base}}/configuration/rules-dsl.html) | A programming language developed specifically for openHAB based on the Xtend. | Long time openHAB users |
23-
| [ECMAScript 5.1]({{base}}/configuration/jsr223.html) | An older version of JavaScript built into Java Virtual Machine 14 and before. It's the language Blockly "compiles" into which can be a powerful learning tool (i.e. build a rule in Blockly and examine the code it generates to see how it works). | Not recommended for use, will go away at sometime soon (Blockly will be updated at that time to use an alternative). |
19+
| Language | Details | Intended Audience |
20+
| -------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- | ----------------------- |
21+
| [Blockly]({{base}}/configuration/blockly/) | See the previous page | Non-developers |
22+
| [Rules DSL]({{base}}/configuration/rules-dsl.html) | A programming language developed specifically for openHAB based on [Xtend](https://eclipse.dev/Xtext/xtend/). | Long time openHAB users |
2423

2524
In addition to these default choices, one can install a number of different languages as an Automation add-on.
26-
Such diverse languages as Python, Ruby, Groovy, Java, and more are available with more to come.
25+
Such diverse languages as [JavaScript](/addons/automation/jsscripting/), [Ruby](/addons/automation/jrubyscripting), [Python](/addons/automation/jythonscripting/), [Groovy](/addons/automation/groovyscripting/), Java, and more are available with more to come.
2726
See the add-on docs for the reference guide and specific information for how to use each individual add-on.
2827
Take note, not all automation add-ons support writing rules in the UI.
2928

30-
For the rest of this tutorial we will use the [JavaScript Scripting add-on](/addons/automation/jsscripting/) which implements ECMAScript 2021 for Script Actions and Script Conditions in UI rules.
29+
For the rest of this tutorial we will show how to use the [JavaScript Scripting add-on](/addons/automation/jsscripting/) which implements ECMAScript 2021, and [Ruby](/addons/automation/jrubyscripting) for Script Actions and Script Conditions in UI rules.
3130
See the add-on's reference for how to write rules in text files which is outside the scope of this tutorial.
3231

3332
## Installation
@@ -52,8 +51,27 @@ These libraries come in various forms and are installed in different ways but th
5251
- abstract some of the sometimes verbose series of steps requires to do something (e.g. access an Item's metadata) into a single function call.
5352

5453
See the automation add-on's reference for how to access and install the Helper Library for your language of choice.
54+
55+
:::: tabs
56+
57+
::: tab JS
58+
5559
In the case of JavaScript Scripting, the Helper Library comes with the add-on (nothing to install separately) and it is automatically imported into your rules for you (advanced users can turn off that auto import in MainUI Settings -> JS Scripting).
5660
To get the latest version of the Helper Library instead of waiting for the next release of OH, it can be installed using `npm`.
61+
62+
:::
63+
64+
::: tab JRuby
65+
66+
The Ruby scripting is implemented using JRuby Scripting add-on.
67+
It will install its helper library by default, and it is automatically imported into your rules.
68+
This can be configured or disabled in Main UI Settings -> JRuby Scripting.
69+
Additional Ruby Gems can be installed by specifying them in the `gems` configuration.
70+
71+
:::
72+
73+
::::
74+
5775
Again, see the add-on's reference for details.
5876

5977
## Creating a Rule
@@ -76,26 +94,51 @@ We will use the same trigger from the Blockly tutorial.
7694

7795
### Then: Actions
7896

79-
Just like in Blockly, we will create a new Action but instead of choosing Blockly, we will choose ECMAScript 2021.
97+
Just like in Blockly, we will create a new Action but instead of choosing Blockly, we will choose `ECMAScript` or `Ruby`.
8098

8199
![select language](images/rules-advanced-select-lang.png)
82100

83101
This will open a blank text field where you can start typing your code.
84102
This text field provides text highlighting and some code completion which helps with coding.
85103

104+
As with the Blockly example, we want to start the rule using a log statement we can see in openhab.log when the rule runs.
105+
106+
:::: tabs
107+
108+
::: tab JS
109+
86110
As previously mentioned, the Helper Library for this language comes with the add-on and is imported by default, so see the [JavaScript Scripting add-on's reference](/addons/automation/jsscripting/) for the full guide on how to do anything you might want to do.
87111
For help with general JavaScript coding, there are tons of tutorials and reference documents on the web a search away.
88112

89-
As with the Blockly example, we want to start the rule using a log statement we can see in openhab.log when the rule runs.
90113
One can either use the [log actions](/addons/automation/jsscripting/#log) but most will find it easiest to use the more JavaScript native [`console`](/addons/automation/jsscripting/#console).
91114

92115
```javascript
93116
console.info('Motion was detected');
94117
```
95118

119+
:::
120+
121+
::: tab JRuby
122+
123+
If you are new to Ruby, check out [Ruby Basics](https://openhab.github.io/openhab-jruby/main/file.ruby-basics.html) for a quick overview of the language.
124+
The Ruby language and the [JRuby Helper Library](https://openhab.github.io/openhab-jruby/) offers a streamlined syntax for writing file-based and UI-based rules, making it easier and more intuitive than RulesDSL, while delivering the full features of the Ruby language.
125+
126+
```ruby
127+
logger.info "Motion was detected"
128+
```
129+
130+
:::
131+
132+
::::
133+
96134
Save and test the rule by running it manually and verify you see this statement in the logs.
97135

98-
Next we want to `sendCommand` to the light to turn it on.
136+
Next we want to send a command to the light to turn it on.
137+
138+
:::: tabs
139+
140+
::: tab JS
141+
99142
Reading the docs we see that access to the Item registry is provided through [`Items`](/addons/automation/jsscripting/#items) where we can get access to a JavaScript Object that represents the Item.
100143
This Object has a `sendCommand()` function that takes the command.
101144

@@ -104,9 +147,36 @@ console.info('Motion was detected');
104147
items.getItem('FrontPorchLight').sendCommand('ON');
105148
```
106149

150+
:::
151+
152+
::: tab JRuby
153+
154+
In JRuby, openHAB Items are represented directly by their names.
155+
You can also access them through the [items](https://openhab.github.io/openhab-jruby/main/OpenHAB/DSL.html#items-class_method) registry so you can find them using a string.
156+
The Item object has a generic [#command](https://openhab.github.io/openhab-jruby/main/OpenHAB/Core/Items/GenericItem.html#command-instance_method) method and also command methods specific for each item type in openHAB.
157+
158+
```ruby
159+
logger.info "Motion was detected"
160+
FrontPorchLight.on # this sends the ON command to the Item
161+
162+
# The following lines do the same thing
163+
# FrontPorchLight.command ON
164+
# items["FrontPorchLight"].on
165+
# items["FrontPorchLight"].comand ON
166+
```
167+
168+
:::
169+
170+
::::
171+
107172
Save and test and verify you see the log statement and the Item receive an ON command.
108173

109174
Now we want to create a Timer to go off in 30 minutes.
175+
176+
:::: tabs
177+
178+
::: tab JS
179+
110180
We find the Timer creation documented under [ScriptExecution](/addons/automation/jsscripting/#scriptexecution-actions).
111181
A Timer will execute a block of code passed to it as the second argument at the time specified by the first argument.
112182

@@ -137,10 +207,37 @@ var lightsOut = function() {
137207
actions.ScriptExecution.createTimer(time.ZonedDateTime.now().plusMinutes(30), lightsOut);
138208
```
139209

210+
:::
211+
212+
::: tab JRuby
213+
214+
Timers are created by calling the [after](https://openhab.github.io/openhab-jruby/main/OpenHAB/DSL.html#after-class_method) method.
215+
It accepts a [Duration](https://openhab.github.io/openhab-jruby/main/OpenHAB/CoreExt/Java/Duration.html), a Ruby [Time](https://docs.ruby-lang.org/en/master/Time.htmll), or a java [ZonedDateTime](https://openhab.github.io/openhab-jruby/main/OpenHAB/CoreExt/Java/ZonedDateTime.html) object to specify when the timer should execute.
216+
Most of the time, a Duration is used, and the helper library offers a [convenient syntax](https://openhab.github.io/openhab-jruby/main/index.html#durations), e.g. `30.minutes`, to create a Duration object.
217+
218+
```ruby
219+
logger.info "Motion was detected"
220+
FrontPorchLight.on
221+
222+
after 30.minutes do
223+
logger.info "No more motion, turning off the light"
224+
FrontPorchLight.off
225+
end
226+
```
227+
228+
:::
229+
230+
::::
231+
140232
Save and test that you see the log statement and the Item receive the `ON` command and 30 minutes later the second log statement and the `OFF` command.
141233
(hint, change the time passed to the timer to something smaller to make testing easier then change it back once things are working).
142234

143235
Now all we are lacking is the ability to reschedule that timer if motion is seen again in the 30 minute period.
236+
237+
:::: tabs
238+
239+
::: tab JS
240+
144241
Looking back at the docs we find the [`cache`](/addons/automation/jsscripting/#cache).
145242
This is a map of key/value pairs that exists outside of the rule.
146243
Given that position it is able to share data between different rules or between runs of the same rule.
@@ -171,18 +268,51 @@ Also notice a line was added to `lightsOut` to delete the entry in the `cache` w
171268
That will cause the rule to create a new timer the next time the rule runs.
172269
It could be coded to reuse the Timer instead which is an exercise for the reader.
173270

271+
:::
272+
273+
::: tab JRuby
274+
275+
In JRuby, an easy way to reschedule the same timer is done by providing a unique `id` to the timer.
276+
This is called a [reentrant timer](https://openhab.github.io/openhab-jruby/main/OpenHAB/DSL.html#reentrant-timers).
277+
278+
The most convenient ID to use is the Item object for which the timer is operating, but you can use anything as the ID, e.g. a String, a number, the rule uid, etc.
279+
280+
```ruby
281+
logger.info "Motion was detected"
282+
FrontPorchLight.on
283+
284+
after 30.minutes, id: FrontPorchLight do |timer|
285+
logger.info "No more motion, turning off the light"
286+
timer.id.off # We can do this because the Timer's id was set to the item object
287+
# It is the same as FrontPorchLight.off
288+
end
289+
```
290+
291+
While it may seem straightforward, the JRuby helper library manages the timer and rescheduling internally to reduce the need for repetitive code.
292+
Full flexibility to work and manipulate the timer is available for more advanced use.
293+
294+
:::
295+
296+
::::
297+
174298
Save and test that the rule sends the on and off commands as described.
175299

176300
### But only if: Conditions
177301

178302
Now we want the rule to only execute between sunset and 23:00.
179-
Create a new Condition and choose ECMAScript 2021 as the language.
303+
Create a new Condition and choose `ECMAScript` or `Ruby` as the language.
304+
This doesn't have to be the same language as the one chosen for the Script Action above.
180305
Just like with the Script Action, the Helper Library is available by default.
181306

182307
As discussed in the Blockly tutorial, the last line of a condition must evaluate to `true` or `false`.
183308
When `true` the rule will run, otherwise it's skipped.
184309

185310
Sunset is available in an Item called `Sunset` and we need to test to see if `now` is between then and `23:00`.
311+
312+
:::: tabs
313+
314+
::: tab JS
315+
186316
In order to do that we need to convert the `Sunset` state to a JS-Joda date time.
187317

188318
Looking at the docs for [`Item`](/addons/automation/jsscripting/#items) we see that the Helper Library `Item` Class will return the String representation of the Item's state by default.
@@ -208,6 +338,30 @@ var endTime = time.ZonedDateTime.now().withHour(23).withMinute(0).withSecond(0).
208338
now.isAfter(sunset) && now.isBefore(endTime)
209339
```
210340

341+
:::
342+
343+
::: tab JRuby
344+
345+
```ruby
346+
Time.now.between? Sunset.state.to_s.."23:00"
347+
348+
# Alternative code:
349+
# ZonedDateTime.now.between? Sunset.state.to_s.."23:00"
350+
# Time.now.between? Sunset.state, Time.parse("23:00") # Two arguments of date/time objects
351+
# Time.now >= Sunset.state && Time.now <= Time.parse("23:00")
352+
# etc.
353+
```
354+
355+
[#between?](https://openhab.github.io/openhab-jruby/main/OpenHAB/CoreExt/Between.html#between%3F-instance_method) helper method is available on all Ruby and Java date, time, and Duration objects.
356+
It takes a Ruby [Range](https://docs.ruby-lang.org/en/master/Range.html) as its argument, or two Date/Time objects to perform an inclusive range comparison.
357+
358+
Note that in JRuby, Date/Time and also DateTimeType objects (in the above example, the item's state) can all work interchangeably without any explicit conversions.
359+
See [Working with Time](https://openhab.github.io/openhab-jruby/main/index.html#time) for more details.
360+
361+
:::
362+
363+
::::
364+
211365
## Advanced Topics
212366

213367
### Libraries
@@ -216,6 +370,10 @@ Most of the languages support installation and importing of third party librarie
216370
Many of the languages will have special tools to download and install these libraries and there might be extra requirements to use them in openHAB.
217371
See the add-on's docs and the forum for details on the automation add-on chosen.
218372

373+
:::: tabs
374+
375+
::: tab JS
376+
219377
For JS Scripting, `npm` is supported for the installation of third party libraries.
220378
Run the `npm` command from the `$OH_CONF/automation/js` folder.
221379
This will create a `node_modules` folder (if it doesn't already exist) and the library will be installed there.
@@ -231,6 +389,52 @@ That keeps it from being modified or overwritten by `npm` when upgrading or inst
231389
There are several third party openHAB specific libraries on `npm` already.
232390
Search the forum for details.
233391

392+
:::
393+
394+
::: tab JRuby
395+
396+
You can install JRuby-compatible Gems from [rubygems.org](https://rubygems.org) by listing them in the JRuby Script add-on [configurations](https://openhab.github.io/openhab-jruby/main/index.html#configuration).
397+
Alternatively, the inline bundler is available from inside your script.
398+
399+
```ruby
400+
gemfile do
401+
source "https://rubygems.org"
402+
gem "faraday", "~> 2.12", ">= 2.12.2"
403+
end
404+
```
405+
406+
[Personal libraries](https://openhab.github.io/openhab-jruby/main/index.html#shared-code) that contain your custom reusable constants, functions, data structures, and classes can be created and used inside your Script Actions and Conditions.
407+
Simply save a `.rb` file that contain your code in `$OH_CONF/automation/ruby/lib/` folder, then `require` it in your script.
408+
409+
For example, a personal library file in `$OH_CONF/automation/ruby/lib/mylibs.rb`:
410+
411+
```ruby
412+
DESTINATION_EMAIL = "myemail@gmail.com"
413+
414+
def broadcast_alert(msg)
415+
Notification.send msg, title: "Alert!"
416+
things["mail:smtp:local"].send_mail(DESTINATION_EMAIL, "OpenHAB Alert", msg)
417+
end
418+
```
419+
420+
Usage in Script Action:
421+
422+
```ruby
423+
require "mylibs"
424+
425+
if event.open?
426+
after 30.minutes, id: event.item do
427+
broadcast_alert "The Garage Door has been left open!"
428+
end
429+
else
430+
timers.cancel(event.item)
431+
end
432+
```
433+
434+
:::
435+
436+
::::
437+
234438
### Debugging
235439

236440
All the same advice from Blockly applies here too.

0 commit comments

Comments
 (0)