From 5a39985420af05bc875e516e0e94ccfc60be3407 Mon Sep 17 00:00:00 2001 From: jimtng <2554958+jimtng@users.noreply.github.com> Date: Mon, 2 Oct 2023 00:01:13 +1000 Subject: [PATCH] [jrubyscripting] Update README.md (#15624) Signed-off-by: Jimmy Tanagra --- .../README.md | 207 ++++++++++++------ 1 file changed, 142 insertions(+), 65 deletions(-) diff --git a/bundles/org.openhab.automation.jrubyscripting/README.md b/bundles/org.openhab.automation.jrubyscripting/README.md index 39686cb12bd..e6b1145b65e 100644 --- a/bundles/org.openhab.automation.jrubyscripting/README.md +++ b/bundles/org.openhab.automation.jrubyscripting/README.md @@ -38,6 +38,7 @@ If you're new to Ruby, you may want to check out [Ruby Basics](https://openhab.g - [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) @@ -61,6 +62,7 @@ If you're new to Ruby, you may want to check out [Ruby Basics](https://openhab.g - [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). @@ -79,7 +81,7 @@ Additional [example rules are available](https://openhab.github.io/openhab-jruby - 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. @@ -95,8 +97,7 @@ Additional [example rules are available](https://openhab.github.io/openhab-jruby ### 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` @@ -104,8 +105,7 @@ Additional [example rules are available](https://openhab.github.io/openhab-jruby ### Using Files -1. Edit `/services/addons.cfg` and ensure that `jrubyscripting` is included in - an uncommented `automation=` list of automations to install. +1. Edit `/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 @@ -146,20 +146,18 @@ org.openhab.automation.jrubyscripting:require=openhab/dsl ### 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: @@ -175,8 +173,7 @@ 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 @@ -185,13 +182,13 @@ The default is to require the helper library. ### 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 @@ -201,7 +198,8 @@ See [this](https://github.com/jruby/jruby/wiki/RedBridge#context-instance-type) ### 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 @@ -210,8 +208,7 @@ See the [JRuby documentation](https://github.com/jruby/jruby/wiki/RedBridge#loca 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 @@ -262,7 +259,8 @@ See [File Based Rules](#file-based-rules) for examples of creating rules within 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 | | ------------- | -------------------------------------------------------------------------------------------- | -------------------------------------- | ---------------------------------------------------- | ---------------------- | @@ -542,9 +540,7 @@ See [unit block](https://openhab.github.io/openhab-jruby/main/OpenHAB/DSL.html#u ##### 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") @@ -559,8 +555,8 @@ Temperature_Item.update(16 | "°C") 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) @@ -708,6 +704,23 @@ thing.enable 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. @@ -740,14 +753,15 @@ Exec.execute_command_line('/path/to/program') ### 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: @@ -780,7 +794,9 @@ sleep 1.5 # sleep for 1.5 seconds 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 @@ -850,7 +866,7 @@ It can be rescheduled to a different duration ```ruby after(3.minutes) do |timer| My_Light.on - my_timer.reschedule(1.minute) + timer.reschedule(1.minute) end ``` @@ -922,22 +938,22 @@ Just be wary of Ruby-only data types (such as Symbols) that won't be accessible 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 @@ -1140,20 +1156,19 @@ Date.today.in_dayset?(:school) # => false ### 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) } @@ -1210,9 +1225,32 @@ rules[rule_uid].enable 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. @@ -1250,14 +1288,26 @@ end ### 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+. @@ -1266,8 +1316,7 @@ 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 @@ -1309,7 +1358,8 @@ When 3 lights out of 10 lights are on, this will produce a formatted state of `3 #### 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]" @@ -1322,6 +1372,31 @@ When the item contains `0 °C`, this will produce a formatted state of `0 °C / 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 @@ -1360,8 +1435,8 @@ See [#changed](https://openhab.github.io/openhab-jruby/main/OpenHAB/DSL/Rules/Bu ##### 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 @@ -1540,13 +1615,13 @@ See [Rule Guards](https://openhab.github.io/openhab-jruby/main/OpenHAB/DSL/Rules ### 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 @@ -1559,8 +1634,8 @@ end #### 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 @@ -1571,9 +1646,9 @@ end #### 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 @@ -1686,7 +1761,7 @@ end ## 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 @@ -1699,4 +1774,6 @@ formatter = DateTimeFormatter.of_pattern("yyyy MM dd") 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 for the full documentation of the **openHAB JRuby Helper Library**.