- [Shared Code](#shared-code)
- [Transformations](#transformations)
- [Profile](#profile)
+ - [Sitemaps](#sitemaps)
- [File Based Rules](#file-based-rules)
- [Basic Rule Structure](#basic-rule-structure)
- [Rule Triggers](#rule-triggers)
- [Dynamic Generation of Rules](#dynamic-generation-of-rules)
- [Hooks](#hooks)
- [Calling Java From JRuby](#calling-java-from-jruby)
+- [Full Documentation](#full-documentation)
Additional [example rules are available](https://openhab.github.io/openhab-jruby/main/file.examples.html), as well as examples of [conversions from DSL and Python rules](https://openhab.github.io/openhab-jruby/main/file.conversions.html).
- Abstract away complexities of openHAB
- Enable all the power of Ruby and openHAB
- Create a Frictionless experience for building automation
-- The common, yet tricky tasks are abstracted and made easy. e.g. creating a timer that automatically reschedules itself.
+- The common, yet tricky tasks are abstracted and made easy, e.g. creating a timer that automatically reschedules itself.
- Tested
- Designed and tested using [Test-Driven Development](https://en.wikipedia.org/wiki/Test-driven_development) with [RSpec](https://rspec.info/)
- Extensible.
### From the User Interface
-1. Go to `Settings -> Add-ons -> Automation` and install the jrubyscripting automation addon
- following the [openHAB instructions](https://www.openhab.org/docs/configuration/addons.html).
+1. Go to `Settings -> Add-ons -> Automation` and install the jrubyscripting automation addon following the [openHAB instructions](https://www.openhab.org/docs/configuration/addons.html).
In openHAB 4.0+ the defaults are set so the next step can be skipped.
1. Go to `Settings -> Other Services -> JRuby Scripting`:
- **Ruby Gems**: `openhab-scripting=~>5.0`
### Using Files
-1. Edit `<OPENHAB_CONF>/services/addons.cfg` and ensure that `jrubyscripting` is included in
- an uncommented `automation=` list of automations to install.
+1. Edit `<OPENHAB_CONF>/services/addons.cfg` and ensure that `jrubyscripting` is included in an uncommented `automation=` list of automations to install.
In openHAB 4.0+ the defaults are set so the next step can be skipped.
1. Configure JRuby openHAB services
### gem_home
-Path to where Ruby Gems will be installed to and loaded from. The directory will be created if necessary.
-You can use `RUBY_ENGINE_VERSION`, `RUBY_ENGINE` and/or `RUBY_VERSION` replacements in this value
-to automatically point to a new directory when the addon is updated with a new version of JRuby.
+Path to where Ruby Gems will be installed to and loaded from.
+The directory will be created if necessary.
+You can use `RUBY_ENGINE_VERSION`, `RUBY_ENGINE` and/or `RUBY_VERSION` replacements in this value to automatically point to a new directory when the addon is updated with a new version of JRuby.
### gems
A comma separated list of [Ruby Gems](https://rubygems.org/) to install.
The default installs the version of the helper for this version of openHAB.
-When overriding the default, be sure to still include the `openhab-scripting` gem in the
-list of gems to install.
+When overriding the default, be sure to still include the `openhab-scripting` gem in the list of gems to install.
-Each gem can have version specifiers which uses
-[pessimistic versioning](https://thoughtbot.com/blog/rubys-pessimistic-operator).
+Each gem can have version specifiers which uses [pessimistic versioning](https://thoughtbot.com/blog/rubys-pessimistic-operator).
Multiple version specifiers can be added by separating them with a semicolon.
Examples:
### check_update
Check RubyGems for updates to the above gems when openHAB starts or JRuby settings are changed.
-Otherwise it will try to fulfil the requirements with locally installed gems, and you can manage them yourself
-with an external Ruby by setting the same GEM_HOME.
+Otherwise it will try to fulfil the requirements with locally installed gems, and you can manage them yourself with an external Ruby by setting the same GEM_HOME.
### require
### rubylib
-Search path for user libraries. Separate each path with a colon (semicolon in Windows).
+Search path for user libraries.
+Separate each path with a colon (semicolon in Windows).
### dependency_tracking
Dependency tracking allows your scripts to automatically reload when one of its dependencies is updated.
-You may want to disable dependency tracking if you plan on editing or updating a shared library,
-but don't want all your scripts to reload until you can test it.
+You may want to disable dependency tracking if you plan on editing or updating a shared library, but don't want all your scripts to reload until you can test it.
### local_context
### local_variables
-Defines how variables are shared between Ruby and Java. Valid values are: `transient`, `persistent`, or `global`.
+Defines how variables are shared between Ruby and Java.
+Valid values are: `transient`, `persistent`, or `global`.
See the [JRuby documentation](https://github.com/jruby/jruby/wiki/RedBridge#local-variable-behavior-options) for options and details.
## Usage
The quickest way to add rules is through the openHAB Web UI.
-Advanced users, or users migrating scripts from existing systems may want to use
-[File Based Scripts](#file-based-scripts) for managing rules using files in the user configuration directory.
+Advanced users, or users migrating scripts from existing systems may want to use [File Based Scripts](#file-based-scripts) for managing rules using files in the user configuration directory.
#### Adding Triggers
When you use "Item event" as trigger (i.e. "[item] received a command", "[item] was updated", "[item] changed"), there is additional context available for the action in a variable called `event`.
-This tables gives an overview of the `event` object for most common trigger types. For full details, explore [OpenHAB::Core::Events](https://openhab.github.io/openhab-jruby/main/OpenHAB/Core/Events.html).
+This tables gives an overview of the `event` object for most common trigger types.
+For full details, explore [OpenHAB::Core::Events](https://openhab.github.io/openhab-jruby/main/OpenHAB/Core/Events.html).
| Property Name | Type | Trigger Types | Description | Rules DSL Equivalent |
| ------------- | -------------------------------------------------------------------------------------------- | -------------------------------------- | ---------------------------------------------------- | ---------------------- |
##### Range checking
-Types that are comparable, such as [StringType](https://openhab.github.io/openhab-jruby/main/OpenHAB/Core/Types/StringType.html), [DateTimeType](https://openhab.github.io/openhab-jruby/main/OpenHAB/Core/Types/DateTimeType.html), [DecimalType](https://openhab.github.io/openhab-jruby/main/OpenHAB/Core/Types/DecimalType.html), [PercentType](https://openhab.github.io/openhab-jruby/main/OpenHAB/Core/Types/PercentType.html),
-include Ruby's [Comparable](https://docs.ruby-lang.org/en/master/Comparable.html) module which provides
-the handy [between?](https://docs.ruby-lang.org/en/master/Comparable.html#method-i-between-3F) method.
+Types that are comparable, such as [StringType](https://openhab.github.io/openhab-jruby/main/OpenHAB/Core/Types/StringType.html), [DateTimeType](https://openhab.github.io/openhab-jruby/main/OpenHAB/Core/Types/DateTimeType.html), [DecimalType](https://openhab.github.io/openhab-jruby/main/OpenHAB/Core/Types/DecimalType.html), [PercentType](https://openhab.github.io/openhab-jruby/main/OpenHAB/Core/Types/PercentType.html), include Ruby's [Comparable](https://docs.ruby-lang.org/en/master/Comparable.html) module which provides the handy [between?](https://docs.ruby-lang.org/en/master/Comparable.html#method-i-between-3F) method.
```ruby
String_Item.update("Freddy")
Temperature_Item.state.between?(20 | "°C", 24 | "°C") # => false
```
-Alternatively, a Ruby [range](https://docs.ruby-lang.org/en/master/Range.html) can be used. This can be
-handy for excluding the end of the range with the `...` operator.
+Alternatively, a Ruby [range](https://docs.ruby-lang.org/en/master/Range.html) can be used.
+This can be handy for excluding the end of the range with the `...` operator.
```ruby
if (5..10).cover?(Number_Item.state)
logger.info "TV enabled: #{thing.enabled?}"
```
+Get Thing's configurations:
+
+```ruby
+server = things["smtp:mail:local"].configuration["hostname"]
+logger.info "Configured SMTP Server: #{server}"
+
+frontporch_cam_ip = things["ipcamera:dahua:frontporch"].configuration["ipAddress"]
+logger.info "Front Porch Camera's IP Address: #{frontporch_cam_ip}"
+```
+
+Get Thing's property:
+
+```ruby
+model_id = things["fronius:meter:mybridge:mymeter"].properties["modelId"]
+logger.info "Fronius Smart Meter model: #{model_id}"
+```
+
### Actions
[openHAB built-in actions](https://www.openhab.org/docs/configuration/actions.html) are available as children of the [Actions](https://openhab.github.io/openhab-jruby/main/OpenHAB/Core/Actions.html) module.
### Logging
-The JRuby Scripting addon has a global `logger` object for logging. To log a message on `INFO` log level:
+The JRuby Scripting addon has a global `logger` object for logging.
+To log a message on `INFO` log level:
```ruby
logger.info("The current time is #{Time.now}")
```
The default logger name for UI rules is `org.openhab.automation.jrubyscripting.script`.
-For file-based rules, it's based on the rule's ID, such as `org.openhab.automation.jrubyscripting.rule.myrule.rb:15`
+For file-based rules, it's based on the rule's ID, such as `org.openhab.automation.jrubyscripting.rule.myrule.rb:15`.
To use a custom logger name:
See Ruby docs on [sleep](https://docs.ruby-lang.org/en/master/Kernel.html#method-i-sleep).
-`sleep` should be avoided if possible. A [delay](https://openhab.github.io/openhab-jruby/main/OpenHAB/DSL/Rules/BuilderDSL.html#delay-instance_method) can be inserted in between two execution blocks to achieve the same result. This delay is implemented with a timer.
+`sleep` should be avoided if possible.
+A [delay](https://openhab.github.io/openhab-jruby/main/OpenHAB/DSL/Rules/BuilderDSL.html#delay-instance_method) can be inserted in between two execution blocks to achieve the same result.
+This delay is implemented with a timer.
This is available only on file-based rules.
```ruby
```ruby
after(3.minutes) do |timer|
My_Light.on
- my_timer.reschedule(1.minute)
+ timer.reschedule(1.minute)
end
```
Get a previously set object with a default value:
```ruby
-counter_object = shared_cache.compute_if_absent(:counter) { { "times" => 0 } }
-logger.info("Count: #{counter_object["times"] += 1}")"
+shared_cache.compute_if_absent(:counter) { 0 } # Initialize with 0 if it didn't exist
+logger.info("Count: #{shared_cache[:counter] += 1}")
```
Get a previously set object, or assign it (this version is subject to race conditions with other scripts):
```ruby
-counter_object = shared_cache[:counter] ||= { "times" => 0 }
-logger.info("Count: #{counter_object["times"] += 1}")"
+shared_cache[:counter] ||= 0
+logger.info("Count: #{shared_cache[:counter] += 1}")
```
-Get a previously set object with a default value, without assigning it (this version has an even longer amount of time between fetchig the value and assigning it):
+Get a previously set object with a default value, without assigning it (this version has an even longer amount of time between fetching the value and assigning it):
```ruby
-count = shared_count.fetch(:counter) { 0 }
-shared_count[:counter] = count + 1
+count = shared_cache.fetch(:counter) { 0 }
+shared_cache[:counter] = count + 1
```
### Time
### Rules, Scripts, and Scenes
-[Rules](https://openhab.github.io/openhab-jruby/main/OpenHAB/Core/Rules/Rule.html), Scenes and Scripts can be accessed using the
-[rules](https://openhab.github.io/openhab-jruby/main/OpenHAB/Core/Rules/Registry.html) object. For example, to execute/trigger a rule:
+[Rules](https://openhab.github.io/openhab-jruby/main/OpenHAB/Core/Rules/Rule.html), Scenes and Scripts can be accessed using the [rules](https://openhab.github.io/openhab-jruby/main/OpenHAB/Core/Rules/Registry.html) object.
+For example, to execute/trigger a rule:
```ruby
rules[rule_uid].trigger
```
-Scenes are rules with a `Scene` tag, and Scripts are rules with a `Script` tag. They can be found
-using their uid just like normal rules, i.e. `rules[uid]`. For convenience, a list of all Scenes are
-available through the enumerable [rules.scenes](https://openhab.github.io/openhab-jruby/main/OpenHAB/Core/Rules/Registry.html#scenes-instance_method),
-and a list of all Scripts through [rules.scripts](https://openhab.github.io/openhab-jruby/main/OpenHAB/Core/Rules/Registry.html#scripts-instance_method).
+Scenes are rules with a `Scene` tag, and Scripts are rules with a `Script` tag.
+They can be found using their uid just like normal rules, i.e. `rules[uid]`.
+For convenience, a list of all Scenes are available through the enumerable [rules.scenes](https://openhab.github.io/openhab-jruby/main/OpenHAB/Core/Rules/Registry.html#scenes-instance_method), and a list of all Scripts through [rules.scripts](https://openhab.github.io/openhab-jruby/main/OpenHAB/Core/Rules/Registry.html#scripts-instance_method).
-Example: All scenes tagged `sunrise` will be triggered at sunrise, and all scenes tagged
-`sunset` will be triggered at sunset. Note: these use the [Terse Rule](https://openhab.github.io/openhab-jruby/main/OpenHAB/DSL/Rules/Terse.html) syntax.
+Example: All scenes tagged `sunrise` will be triggered at sunrise, and all scenes tagged `sunset` will be triggered at sunset.
+Note: these use the [Terse Rule](https://openhab.github.io/openhab-jruby/main/OpenHAB/DSL/Rules/Terse.html) syntax.
```ruby
channel("astro:sun:home:rise#event") { rules.scenes.tagged("sunrise").each(&:trigger) }
rules[rule_uid].disable
```
+#### Passing Values to Rules
+
+A rule/script may be given additional context/data by the caller.
+This additional data is available within the rule by referring to the names of the context variable.
+This is applicable to both UI rules and file-based rules.
+
+Within the script/rule body (either UI or file rule)
+
+```ruby
+script id: "check_temp" do
+ if CPU_Temperature.state > maxTemperature
+ logger.warn "The CPU is overheating!"
+ end
+end
+```
+
+The above script can be executed, passing it the `maxTemperature` argument from any supported scripting language, e.g.:
+
+```ruby
+rules["check_temp"].trigger(maxTemperature: 80 | "°C")
+```
+
### Gems
-[Bundler](https://bundler.io/) is integrated, enabling any [Ruby gem](https://rubygems.org/) compatible with JRuby to be used within rules. This permits easy access to the vast ecosystem of libraries within the Ruby community.
+[Bundler](https://bundler.io/) is integrated, enabling any [Ruby gem](https://rubygems.org/) compatible with JRuby to be used within rules.
+This permits easy access to the vast ecosystem of libraries within the Ruby community.
Gems are available using the [inline bundler syntax](https://bundler.io/guides/bundler_in_a_single_file_ruby_script.html).
The require statement can be omitted.
### Transformations
+#### Using openHAB Transformations
+
+Existing openHAB transformations can also be used by calling the [transform](https://openhab.github.io/openhab-jruby/main/OpenHAB/DSL.html#transform-class_method) method.
+This enables the use of any transformations from the `/transform` folder or managed through the UI, such as MAP, JSONPATH, Jinja Transformation, etc.
+
+```ruby
+# Convert OPEN/CLOSED to Online/Offline using availability.map
+# OPEN=Online
+# CLOSED=OFFLINE
+logger.info transform(:map, "availability.map", LivingRoom_Switch_Availability.state)
+```
+
+#### Writing Custom Transformations in Ruby
+
This add-on also provides the necessary infrastructure to use Ruby for writing [transformations](https://www.openhab.org/docs/configuration/transformations.html).
The main value to be transformed is given to the script in a variable called `input`.
Note that the values are passed to the transformation as Strings even for numeric items and data types.
-**Note**: In openHAB 3.4, due to an [issue](https://github.com/jruby/jruby/issues/5876) in the current version of JRuby,
-you will need to begin your script with `input ||= nil` (and `a ||= nil` etc. for additional query variables) so that
-JRuby will recognize the variables as variables--rather than method calls--when it's parsing the script.
+**Note**: In openHAB 3.4, due to an [issue](https://github.com/jruby/jruby/issues/5876) in the current version of JRuby, you will need to begin your script with `input ||= nil` (and `a ||= nil` etc. for additional query variables) so that JRuby will recognize the variables as variables--rather than method calls--when it's parsing the script.
Otherwise you will get errors like `(NameError) undefined local variable or method 'input' for main:Object`.
This is not necessary in openHAB 4.0+.
Once the addon is installed, you can create a Ruby file in the `$OPENHAB_CONF/transform` directory, with the extension `.rb`.
When referencing the file, you need to specify the `RB` transform: `RB(mytransform.rb):%s`.
-You can also specify additional variables to be set in the script using a URI-like query syntax: `RB(mytransform.rb?a=1&b=c):%s`
-in order to share a single script with slightly different parameters for different items.
+You can also specify additional variables to be set in the script using a URI-like query syntax: `RB(mytransform.rb?a=1&b=c):%s` in order to share a single script with slightly different parameters for different items.
##### Example: Display the wind direction in degrees and cardinal direction
#### Inline Transformations
-Inline transformations are supported too. For example, to display the temperature in both °C and °F:
+Inline transformations are supported too.
+For example, to display the temperature in both °C and °F:
```Xtend
Number:Temperature Outside_Temperature "Outside Temperature [RB(| input.to_f.|('°C').then { |t| %(#{t.format('%d °C')} / #{t.to_unit('°F').format('%d °F')}) } ):%s]"
You can create an openHAB profile in JRuby that can be applied to item channel links.
For more details, see [#profile](https://openhab.github.io/openhab-jruby/main/OpenHAB/DSL.html#profile-class_method).
+### Sitemaps
+
+Sitemaps can be created via [sitemaps.build](https://openhab.github.io/openhab-jruby/main/OpenHAB/Core/Sitemaps/Provider.html#build-instance_method).
+
+```ruby
+sitemaps.build do
+ sitemap "default", "My Residence" do
+ frame label: "Control" do
+ text label: "Climate", icon: "if:mdi:home-thermometer-outline" do
+ frame label: "Main Floor" do
+ text item: MainFloor_AmbTemp
+ switch item: MainFloorThermostat_TargetMode, label: "Mode", mappings: %w[off auto cool heat]
+ setpoint item: MainFloorThermostat_SetPoint, label: "Set Point", visibility: "MainFloorThermostat_TargetMode!=off"
+ end
+ frame label: "Basement" do
+ text item: Basement_AmbTemp
+ switch item: BasementThermostat_TargetMode, label: "Mode", mappings: { OFF: "off", COOL: "cool", HEAT: "heat" }
+ setpoint item: BasementThermostat_SetPoint, label: "Set Point", visibility: "BasementThermostat_TargetMode!=off"
+ end
+ end
+ end
+ end
+end
+```
+
## File Based Rules
### Basic Rule Structure
##### Detecting Change Duration
-Only execute a rule when an item state changed and stayed the same for a period of time. This method
-can only be done using a file-based rule.
+Only execute a rule when an item state changed and stayed the same for a period of time.
+This method can only be done using a file-based rule.
```ruby
rule "Garage Door Alert" do
### Rule Executions
Execution blocks are executed when a rule is triggered and all the rule conditions are met.
-Multiple execution blocks can be specified. This can be useful especially when using a delay execution
-block inbetween two run or triggered blocks.
+Multiple execution blocks can be specified.
+This can be useful especially when using a delay execution block inbetween two run or triggered blocks.
#### Run Execution Block
-A run execution block is the most commonly used execution block. It provides the full [event object](#event-object)
-to the block.
+A run execution block is the most commonly used execution block.
+It provides the full [event object](#event-object) to the block.
```ruby
rule "Rule with a run block" do
#### Triggered Execution Block
-A triggered execution block passes the `TriggeringItem` object directly to the block. It is handy when combined with
-Ruby's pretzel-colon operator to act directly on the object.
+A triggered execution block passes the `TriggeringItem` object directly to the block.
+It is handy when combined with Ruby's pretzel-colon operator to act directly on the object.
```ruby
rule "Limit the duration of TV watching" do
#### Delay Execution Block
-A delay exection block is useful for adding a delay inbetween rule executions or even at the beginning of the
-trigger event without having to manually create a timer. Unlike `sleep`, a delay block does not block
-the current executing thread. It actually sets a timer for you behind the scenes.
+A delay exection block is useful for adding a delay inbetween rule executions or even at the beginning of the trigger event without having to manually create a timer.
+Unlike `sleep`, a delay block does not block the current executing thread.
+It actually sets a timer for you behind the scenes.
```ruby
rule "Check for offline things 15 minutes after openHAB had started" do
## Calling Java From JRuby
-JRuby can access almost any Java object that's available in the current JVM.
+JRuby can [access almost any Java object](https://github.com/jruby/jruby/wiki/CallingJavaFromJRuby) that's available in the current JVM.
This is how the library is implemented internally.
```ruby
formatter = java.time.format.DateTimeFormatter.of_pattern("yyyy MM dd")
```
-See [full documentation from JRuby](https://github.com/jruby/jruby/wiki/CallingJavaFromJRuby)
+## Full Documentation
+
+Visit <https://openhab.github.io/openhab-jruby/> for the full documentation of the **openHAB JRuby Helper Library**.