From 9f221e550b83cc52e1e523a924d42e7cc7fc2e4b Mon Sep 17 00:00:00 2001 From: jimtng <2554958+jimtng@users.noreply.github.com> Date: Fri, 14 Apr 2023 16:09:51 +1000 Subject: [PATCH] [jrubyscripting] Update README.md (#14798) Signed-off-by: Jimmy Tanagra --- .../README.md | 317 +++++++++++------- 1 file changed, 195 insertions(+), 122 deletions(-) diff --git a/bundles/org.openhab.automation.jrubyscripting/README.md b/bundles/org.openhab.automation.jrubyscripting/README.md index 78711efe6e2..d689fd83be9 100644 --- a/bundles/org.openhab.automation.jrubyscripting/README.md +++ b/bundles/org.openhab.automation.jrubyscripting/README.md @@ -1,7 +1,7 @@ -# JRuby Scripting +# JRuby Scripting This add-on provides [JRuby](https://www.jruby.org/) scripting language for automation rules. Also included is [openhab-scripting](https://openhab.github.io/openhab-jruby/), a fairly high-level Ruby gem to support automation in openHAB. @@ -33,8 +33,11 @@ If you're new to Ruby, you may want to check out [Ruby Basics](https://openhab.g - [Cache](#cache) - [Time](#time) - [Ephemeris](#ephemeris) + - [Rules, Scripts, and Scenes](#rules-scripts-and-scenes) - [Gems](#gems) - [Shared Code](#shared-code) + - [Transformations](#transformations) + - [Profile](#profile) - [File Based Rules](#file-based-rules) - [Basic Rule Structure](#basic-rule-structure) - [Rule Triggers](#rule-triggers) @@ -54,11 +57,9 @@ If you're new to Ruby, you may want to check out [Ruby Basics](https://openhab.g - [Triggered Execution Block](#triggered-execution-block) - [Delay Execution Block](#delay-execution-block) - [Terse Rules](#terse-rules) - - [Rule Manipulations](#rule-manipulations) - [Early Exit From a Rule](#early-exit-from-a-rule) - [Dynamic Generation of Rules](#dynamic-generation-of-rules) - [Hooks](#hooks) - - [Transformations](#transformations) - [Calling Java From JRuby](#calling-java-from-jruby) Additional [example rules are available](https://openhab.github.io/openhab-jruby/5.0/file.examples.html), as well as examples of [conversions from DSL and Python rules](https://openhab.github.io/openhab-jruby/5.0/file.conversions.html). @@ -71,7 +72,7 @@ Additional [example rules are available](https://openhab.github.io/openhab-jruby - Rich ecosystem of tools, including things like Rubocop to help developers write clean code and RSpec to test the libraries. - Ruby is really good at letting one express intent and create a DSL to make that expression easier. -### Design points +### Design points - Create an intuitive method of defining rules and automation - Rule language should "flow" in a way that you can read the rules out loud @@ -87,12 +88,12 @@ Additional [example rules are available](https://openhab.github.io/openhab-jruby ## Installation -### Prerequisites +### Prerequisites 1. openHAB 3.4+ 1. The JRuby Scripting Language Addon -### From the User Interface +### 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). @@ -101,7 +102,7 @@ Additional [example rules are available](https://openhab.github.io/openhab-jruby - **Ruby Gems**: `openhab-scripting=~>5.0.0` - **Require Scripts**: `openhab/dsl` (not required, but recommended) -### Using Files +### Using Files 1. Edit `/services/addons.cfg` and ensure that `jrubyscripting` is included in an uncommented `automation=` list of automations to install. @@ -125,16 +126,16 @@ This allows the use of [items](https://openhab.github.io/openhab-jruby/5.0/OpenH This functionality can be disabled for users who prefer to manage their own gems and `require`s via the add-on configuration options. Simply change the `gems` and `require` configuration settings. -| Parameter | Description | -| --------------------- | ---------------------------------------------------------------------------------------------------------- | +| Parameter | Description | +| --------------------- | -------------------------------------------------------------------------------------------------------- | | `gem_home` | The path to store Ruby Gems.

Default: `$OPENHAB_CONF/automation/ruby/.gem/RUBY_ENGINE_VERSION` | -| `gems` | A list of gems to install.

Default: `openhab-scripting=~>5.0.0` | -| `check_update` | Check for updated version of `gems` on start up or settings change.

Default: `true` | -| `require` | List of scripts to be required automatically.

Default: `openhab/dsl` | -| `rubylib` | Search path for user libraries.

Default: `$OPENHAB_CONF/automation/ruby/lib` | -| `dependency_tracking` | Enable dependency tracking.

Default: `true` | -| `local_context` | See notes below.

Default: `singlethread` | -| `local_variables` | See notes below.

Default: `transient` | +| `gems` | A list of gems to install.

Default: `openhab-scripting=~>5.0.0` | +| `check_update` | Check for updated version of `gems` on start up or settings change.

Default: `true` | +| `require` | List of scripts to be required automatically.

Default: `openhab/dsl` | +| `rubylib` | Search path for user libraries.

Default: `$OPENHAB_CONF/automation/ruby/lib` | +| `dependency_tracking` | Enable dependency tracking.

Default: `true` | +| `local_context` | See notes below.

Default: `singlethread` | +| `local_variables` | See notes below.

Default: `transient` | When using file-based configuration, these parameters must be prefixed with `org.openhab.automation.jrubyscripting:`, for example: @@ -143,13 +144,13 @@ org.openhab.automation.jrubyscripting:gems=openhab-scripting=~>5.0 org.openhab.automation.jrubyscripting:require=openhab/dsl ``` -### gem_home +### 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. -### gems +### gems A comma separated list of [Ruby Gems](https://rubygems.org/) to install. @@ -171,34 +172,34 @@ Examples: | `openhab-scripting=~>5.0, faraday=~>2.7;>=2.7.4` | install `openhab-scripting` gem version 5.x and `faraday` gem version 2.7.4 or higher, but less than 3.0 | | `gem1= >= 2.2.1; <= 2.2.5` | install `gem1` gem version 2.2.1 or above, but less than or equal to version 2.2.5 | -### check_update +### 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. -### require +### require A comma separated list of script names to be required by the JRuby Scripting Engine at the beginning of user scripts. The default is to require the helper library. -### rubylib +### rubylib Search path for user libraries. Separate each path with a colon (semicolon in Windows). -### dependency_tracking +### 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. -### local_context +### local_context The local context holds Ruby runtime, name-value pairs for sharing variables between Java and Ruby. Valid values are: `singleton`, `threadsafe`, `singlethread`, or `concurrent`. See [this](https://github.com/jruby/jruby/wiki/RedBridge#context-instance-type) for options and details. -### local_variables +### local_variables 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. @@ -212,13 +213,13 @@ 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. -#### Adding Triggers +#### Adding Triggers Using the openHAB UI, first create a new rule and set a trigger condition. ![openHAB Rule Configuration](docs/images/rule-config.png) -#### Adding Actions +#### Adding Actions Select "Add Action" and then select "Run Script" with "Ruby". This will bring up an empty script editor where you can enter your JavaScript. @@ -263,8 +264,8 @@ When you use "Item event" as trigger (i.e. "[item] received a command", "[item] 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/5.0/OpenHAB/Core/Events.html). -| Property Name | Type | Trigger Types | Description | Rules DSL Equivalent | -| ------------- | -------------------------------------------- | -------------------------------------- | ---------------------------------------------------- | ---------------------- | +| Property Name | Type | Trigger Types | Description | Rules DSL Equivalent | +| ------------- | ------------------------------------------------------------------------------------------- | -------------------------------------- | ---------------------------------------------------- | ---------------------- | | `state` | [State](https://openhab.github.io/openhab-jruby/5.0/OpenHAB/Core/Types/State.html) or `nil` | `[item] changed`, `[item] was updated` | State that triggered event | `triggeringItem.state` | | `was` | [State](https://openhab.github.io/openhab-jruby/5.0/OpenHAB/Core/Types/State.html) or `nil` | `[item] changed` | Previous state of Item or Group that triggered event | `previousState` | | `command` | [Command](https://openhab.github.io/openhab-jruby/5.0/OpenHAB/Core/Types/Command.html) | `[item] received a command` | Command that triggered event | `receivedCommand` | @@ -433,7 +434,7 @@ Get a sorted list of Group members matching a condition: sorted_items_by_battery_level = gBattery.members .select(&:state?) # only include non NULL / UNDEF members .select { |item| item.state < 20 } # select only those with low battery - .sort_by(&:state) + .sort_by(&:state) ``` Get a list of values mapped from the members of a group: @@ -463,7 +464,7 @@ My_Item << ON Note: all possible commands are supported on the corresponding item types, e.g. `on`, `off`, `up`, `down`, `play`, `pause`, `stop`, etc. For more details, see the individual item classes under [OpenHAB::Core::Items](https://openhab.github.io/openhab-jruby/5.0/OpenHAB/Core/Items.html). -##### Sending Commands to an Item Only When Its State is Different +##### Sending Commands to an Item Only When Its State is Different ```ruby My_Item.ensure.on @@ -474,7 +475,7 @@ My_Item.ensure << ON logger.info("Turning off the light") if My_Item.ensure.off ``` -##### Timed Commands +##### Timed Commands A [Timed Command](https://openhab.github.io/openhab-jruby/5.0/OpenHAB/DSL/Items/TimedCommand.html) is similar to the openHAB Item's [expire parameter](https://www.openhab.org/docs/configuration/items.html#parameter-expire) but it offers more flexibility. It removes the need to manually create a timer. @@ -523,7 +524,7 @@ if My_Item.state? end ``` -##### Comparing Item's State +##### Comparing Item's State ```ruby String_Item.state == 'test string' @@ -532,14 +533,14 @@ items['Number_Item'].state == 10 # Compare Quantity Types Temperature_Item.state > 24 | '°C' -Indoor_Temperature.state > Outdoor_Temperature.state +Indoor_Temperature.state > Outdoor_Temperature.state Indoor_Temperature.state > Outdoor_Temperature.state + 5 | '°C' Indoor_Temperature.state - Outdoor_Temperature.state > 5 | '°C' ``` See [unit block](https://openhab.github.io/openhab-jruby/5.0/OpenHAB/DSL.html#unit-class_method) -##### Range checking +##### Range checking Types that are comparable, such as [StringType](https://openhab.github.io/openhab-jruby/5.0/OpenHAB/Core/Types/StringType.html), [DateTimeType](https://openhab.github.io/openhab-jruby/5.0/OpenHAB/Core/Types/DateTimeType.html), [DecimalType](https://openhab.github.io/openhab-jruby/5.0/OpenHAB/Core/Types/DecimalType.html), [PercentType](https://openhab.github.io/openhab-jruby/5.0/OpenHAB/Core/Types/PercentType.html), include Ruby's [Comparable](https://docs.ruby-lang.org/en/master/Comparable.html) module which provides @@ -550,7 +551,7 @@ String_Item.update("Freddy") String_Item.state.between?("E", "G") # => true Number_Item.update(10) -if Number_Item.state.between?(5, 20) +if Number_Item.state.between?(5, 20) logger.info "Number_Item falls within the expected range" end @@ -569,7 +570,7 @@ end ((20|"°C")..(24|"°C")).cover?(Temperature_Item.state) ``` -##### Loose Type Comparisons +##### Loose Type Comparisons Some openHAB item types can accept different command types. For example, a [DimmerItem](https://openhab.github.io/openhab-jruby/5.0/OpenHAB/Core/Items/DimmerItem.html) can accept a command with an [OnOffType](https://openhab.github.io/openhab-jruby/5.0/OpenHAB/Core/Types/OnOffType.html), [IncreaseDecreaseType](https://openhab.github.io/openhab-jruby/5.0/OpenHAB/Core/Types/IncreaseDecreaseType.html) or a [PercentType](https://openhab.github.io/openhab-jruby/5.0/OpenHAB/Core/Types/PercentType.html). @@ -798,7 +799,7 @@ end When a script is unloaded, all created timers are automatically cancelled. -#### Accessing Variables +#### Accessing Variables You can access all variables of the current context in the created timers. @@ -816,7 +817,7 @@ end my_var = "Hello mutation!" # When the timer runs, it will log "Hello mutation!" instead of "Hello world!" ``` -#### Reschedule a Timer +#### Reschedule a Timer A timer can be rescheduled inside the timer body @@ -855,7 +856,7 @@ rule 'cancel timer' do end ``` -#### Manage Multiple Timers +#### Manage Multiple Timers Multiple timers can be managed in the traditional way by storing the timer objects in a Hash: @@ -863,7 +864,7 @@ Multiple timers can be managed in the traditional way by storing the timer objec @timers ||= {} if @timers[event.item] - @timers[event.item].reschedule + @timers[event.item].reschedule else @timers[event.item] = after 3.minutes do # Use the triggering item as the timer ID event.item.off @@ -947,7 +948,7 @@ Several options are available for time related code, including but not limited t - Ruby [Time](https://docs.ruby-lang.org/en/master/Time.html) - represents a specific instant with a date and time - Ruby [DateTime](https://docs.ruby-lang.org/en/master/DateTime.html) - represents a specific instant with a date and time -#### Durations +#### Durations Ruby [integers](https://docs.ruby-lang.org/en/master/Integer.html) and [floats](https://docs.ruby-lang.org/en/master/Float.html) are extended with several methods to support durations. These methods create a new [Duration](https://openhab.github.io/openhab-jruby/5.0/OpenHAB/CoreExt/Java/Duration.html) or [Period](https://openhab.github.io/openhab-jruby/5.0/OpenHAB/CoreExt/Java/Period.html) object that is used by the [every](https://openhab.github.io/openhab-jruby/5.0/OpenHAB/DSL/Rules/BuilderDSL.html#every-instance_method) trigger, [delay](https://openhab.github.io/openhab-jruby/5.0/OpenHAB/DSL/Rules/BuilderDSL.html#delay-instance_method) block, the for option of [changed](https://openhab.github.io/openhab-jruby/5.0/OpenHAB/DSL/Rules/BuilderDSL.html#changed-instance_method) triggers, and [timers](https://openhab.github.io/openhab-jruby/5.0/OpenHAB/Core/Timer.html). @@ -975,7 +976,7 @@ rule "Timer example" do end ``` -#### Time Comparisons, Conversions, and Arithmetic +#### Time Comparisons, Conversions, and Arithmetic Comparisons, conversions and arithmetic are automatic between Java and Ruby types. Note that anytime you do a comparison between a type with more specific data, and a type missing specific data, the comparison is done as if the more specific data is at the beginning of its period. @@ -1044,7 +1045,7 @@ end # Comparing Time against ZonedDateTime with `>` sunset = things["astro:sun:home"].get_event_time("SUN_SET", nil, nil) -if Time.now > sunset +if Time.now > sunset logger.info "it is after sunset" end @@ -1059,7 +1060,7 @@ elapsed_time = Time.now - Motion_Sensor.last_update elapsed_time = ZonedDateTime.now - Motion_Sensor.last_update # Using `-` operator with ZonedDateTime -# Comparing two ZonedDateTime using `<` +# Comparing two ZonedDateTime using `<` Motion_Sensor.last_update < Light_Item.last_update - 10.minutes # is the same as: Motion_Sensor.last_update.before?(Light_Item.last_update.minus_minutes(10)) @@ -1078,7 +1079,7 @@ Time.at(1669684403).to_zoned_date_time java.time.Instant.of_epoch_second(1669684403).at_zone(ZoneId.system_default) ``` -#### Ranges +#### Ranges Ranges of date time objects work as expected. Make sure to use `#cover?` instead of `#include?` to do a simple comparison, instead of generating an array and searching it linearly. @@ -1130,6 +1131,78 @@ Date.today.weekend? # => true Date.today.in_dayset?(:school) # => false ``` +### Rules, Scripts, and Scenes + +[Rules](https://openhab.github.io/openhab-jruby/5.0/OpenHAB/Core/Rules/Rule.html), Scenes and Scripts can be accessed using the +[rules](https://openhab.github.io/openhab-jruby/5.0/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/5.0/OpenHAB/Core/Rules/Registry.html#scenes-instance_method), +and a list of all Scripts through [rules.scripts](https://openhab.github.io/openhab-jruby/5.0/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/5.0/OpenHAB/DSL/Rules/Terse.html) syntax. + +```ruby +channel("astro:sun:home:rise#event") { rules.scenes.tagged("sunrise").each(&:trigger) } +channel("astro:sun:home:set#event") { rules.scenes.tagged("sunset").each(&:trigger) } +``` + +Or it can be written as one rule with the help of [trigger attachments](https://openhab.github.io/openhab-jruby/5.0/OpenHAB/DSL/Rules/BuilderDSL.html#Triggers-group). + +```ruby +rule "Activate scenes at sunset/sunrise" do + channel "astro:sun:home:rise#event", attach: "sunrise" + channel "astro:sun:home:set#event", attach: "sunset" + run { |event| rules.scenes.tagged(event.attachment).each(&:trigger) } +end +``` + +Get the UID of a Rule + +```ruby +rule_obj = rule 'my rule name' do + received_command My_Item + run do + # rule code here + end +end + +rule_uid = rule_obj.uid +``` + +A rule's UID can also be specified at rule creation + +```ruby +rule "my rule name", id: "my_unique_rule_uid" do + # ... +end + +# or +rule "my rule name" do + uid "my_unique_rule_uid" + # ... +end +``` + +Get the UID of a Rule by Name + +```ruby +rule_uid = rules.find { |rule| rule.name == 'This is the name of my rule' }.uid +``` + +Enable or Disable a Rule by UID + +```ruby +rules[rule_uid].enable +rules[rule_uid].disable +``` + ### 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. @@ -1168,6 +1241,81 @@ def my_lib_version end ``` +### Transformations + +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. +Otherwise you will get errors like `(NameError) undefined local variable or method 'input' for main:Object`. +This is not necessary in openHAB 4.0+. + +#### File Based Transformations + +Once the addon is installed, you can create a Ruby file in the `$OPENHAB_CONF/transform` directory, with the extension `.script`. +It's important that the extension is `.script` so that the core `SCRIPT` transform service will recognize it. +When referencing the file, you need to specify the `SCRIPT` transform, with `rb` as the script type: `SCRIPT(rb:mytransform.script):%s`. + +You can also specify additional variables to be set in the script using a URI-like query syntax: `SCRIPT(rb:mytransform.script?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 + +`weather.items` + +```Xtend +Number:Angle Exterior_WindDirection "Wind Direction [SCRIPT(rb:compass.script):%s]" +``` + +`compass.script` + +```ruby +DIRECTIONS = %w[N NE E SE S SW W NW N].freeze + +if input.nil? || input == "NULL" || input == "UNDEF" + "-" +else + cardinal = DIRECTIONS[(input.to_f / 45).round] + "#{cardinal} (#{input.to_f.round}°)" +end +``` + +Given a state of `82 °`, this will produce a formatted state of `E (82°)`. + +##### Example: Display the number of lights that are on/off within a group + +```Xtend +Group gIndoorLights "Indoor Lights [SCRIPT(rb:group_count.script?group=gIndoorLights):%s]" +Group gOutdoorLights "Outdoor Lights [SCRIPT(rb:group_count.script?group=gOutdoorLights):%s]" +``` + +`group_count.script` + +```ruby +items[group].all_members.then { |all| "#{all.select(&:on?).size}/#{all.size}" } +``` + +When 3 lights out of 10 lights are on, this will produce a formatted state of `3/10` + +#### Inline Transformations + +Inline transformations are supported too. For example, to display the temperature in both °C and °F: + +```Xtend +Number:Temperature Outside_Temperature "Outside Temperature [SCRIPT(rb:| input.to_f.|('°C').then { |t| %(#{t.format('%d °C')} / #{t.to_unit('°F').format('%d °F')}) } ):%s]" +``` + +When the item contains `0 °C`, this will produce a formatted state of `0 °C / 32 °F`. + +### Profile + +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/5.0/OpenHAB/DSL.html#profile-class_method). + ## File Based Rules ### Basic Rule Structure @@ -1204,7 +1352,7 @@ end See [#changed](https://openhab.github.io/openhab-jruby/5.0/OpenHAB/DSL/Rules/BuilderDSL.html#changed-instance_method) -##### Detecting Change Duration +##### 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. @@ -1300,7 +1448,7 @@ end See [#cron](https://openhab.github.io/openhab-jruby/5.0/OpenHAB/DSL/Rules/BuilderDSL.html#cron-instance_method) -##### `every` Trigger +##### `every` Trigger ```ruby rule "run every day" do @@ -1444,48 +1592,6 @@ received_command(My_Switch, to: ON) { My_Light.on } See [Terse Rules](https://openhab.github.io/openhab-jruby/5.0/OpenHAB/DSL/Rules/Terse.html) for full details. -### Rule Manipulations - -Get the UID of a Rule - -```ruby -rule_obj = rule 'my rule name' do - received_command My_Item - run do - # rule code here - end -end - -rule_uid = rule_obj.uid -``` - -A rule's UID can also be specified at rule creation - -```ruby -rule "my rule name", id: "my_unique_rule_uid" do - # ... -end -``` - -Get the UID of a Rule by Name - -```ruby -rule_uid = rules.find { |rule| rule.name == 'This is the name of my rule' }.uid -``` - -Enable or Disable a Rule by UID - -```ruby -rules[rule_uid].enable -rules[rule_uid].disable -``` - -Run a Rule by UID - -```ruby -rules[rule_uid].trigger -``` - ### Early Exit From a Rule You can use `next` within a file-based rule, because it's in a block: @@ -1572,39 +1678,6 @@ script_unloaded do end ``` -### Transformations - -This add-on also provides the necessary infrastructure to use Ruby for writing [transformations](https://www.openhab.org/docs/configuration/transformations.html). -Once the addon is installed, you can create a Ruby file in the `$OPENHAB_CONF/transform` directory, with the extension `.script`. -It's important that the extension is `.script` so that the core `SCRIPT` transform service will recognize it. -When referencing the file, you need to specify the `SCRIPT` transform, with `rb` as the script type: `SCRIPT(rb:mytransform.script):%s`. -You can also specify additional variables to be set in the script using a URI-like query syntax: `SCRIPT(rb:mytransform.script?a=1b=c):%s` in order to share a single script with slightly different parameters for different items. - -**Note**: 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`. - -`compass.script` - -```ruby -input ||= nil -DIRECTIONS = %w[N NE E SE S SW W NW N].freeze - -if input.nil? || input == "NULL" || input == "UNDEF" - "-" -else - cardinal = DIRECTIONS[(input.to_f / 45).round] - "#{cardinal} (#{input.to_i}°)" -end -``` - -`weather.items` - -```Xtend -Number:Angle Exterior_WindDirection "Wind Direction [SCRIPT(rb:compass.script):%s]" -``` - -Given a state of `82 °`, this will produce a formatted state of `E (82°)`. - ## Calling Java From JRuby JRuby can access almost any Java object that's available in the current JVM.