OpenHAB JRuby Scripting
The OpenHAB JRuby scripting helpers bring the power of the Ruby language to OpenHAB. Rather than being a pure pass-through to OpenHAB, they provide a Ruby-like experience when building automation rules within OpenHAB.
Discussion
Please see this thread on the OpenHAB forum for further discussion. Ideas and suggestions are welcome.
Quick Examples
The following examples are for file-based rules but most of them are applicable to GUI rules as well.
Trigger when an item changed state
rule 'Turn on light when sensor changed to open' do
changed Door_Sensor, to: OPEN
run { Cupboard_Light.on }
end
Use multiple triggers
rule 'Control light based on multiple doors' do
changed Door_Sensor1, to: OPEN
changed Door_Sensor2, to: OPEN
run { Cupboard_Light.on }
end
# Which is the same as:
rule 'Control light based on multiple doors' do
changed Door_Sensor1, Door_Sensor2, to: OPEN
run { Cupboard_Light.on }
end
Check against multiple states
rule 'Control light based on door state' do
changed Door_Sensor, to: [OPEN, CLOSED]
run { Cupboard_Light << Door_Sensor.open? } # Send a boolean command to a Switch Item
end
Trigger when a group member changed state:
# Assumption: Motion sensor items are named using the pattern RoomName_Motion
# and Light switch items are named with the pattern RoomName_Light
rule 'Generic motion rule' do
changed Motion_Sensors.members, to: ON
run do |event|
light = items[event.item.name.sub('_Motion', '_Light')] # Lookup item name from a string
light&.on
end
end
Various ways of sending a command to an item
Light1 << ON
Light1.on
Rollershutter1.up
ColorItem1.command '#ffff00'
DimmerItem1 << 100
Set_Temperature << '24 °C'
Dealing with Number Items
# Items:
# Number:Temperature Outside_Temperature e.g. 28 °C
# Number:Temperature Inside_Temperature e.g. 22 °C
temperature_difference = Outside_Temperature - Inside_Temperature
logger.info("Temperature difference: #{temperature_difference}") # "Temperature difference: 6 °C"
# Items:
# Number:Power Solar_Panel_Power
# Number:Power Load_Power
# Number:Power Excess_Power
Excess_Power.update(Solar_Panel_Power - Load_Power)
Detect change duration without creating an explicit timer
rule 'Warn when garage door is open a long time' do
changed Garage_Door, to: OPEN, for: 15.minutes
run { say "Warning, the garage door is open" } # call TTS to the default audio sink
end
Use timers
Timers are created using after
with an easier way to specify when it should execute, based on duration syntax, e.g. 10.minutes
instead of using ZonedDateTime.
rule 'simple timer' do
changed Watering_System, to: ON
run do
after(5.minutes) { Watering_System.off }
end
end
Rescheduling timers, the traditional way
@timer = nil # variables starting with @ are instance variables
rule 'reschedule timer' do
updated Motion_Sensor, to: OPEN
run do
Light_Item.on
if (@timer.nil?)
@timer = after(5.minutes) { Light_Item.off } # This is the equivalent of createTimer() in rulesdsl
else
@timer.reschedule # This automatically reschedules it for the original duration (5 minutes)
# To reschedule it a different duration, use @timer.reschedule 10.minutes
end
end
end
rule 'cancel timer' do
changed Light_Item, to: OFF
run { @timer&.cancel }
end
Or use the timer “reentrant” feature to achieve the same thing
rule 'automatic reentrant timer' do
updated Motion_Sensor, to: OPEN
run do
Light_Item.ensure.on # 'ensure' only sends the command if it's not already on
# Using a unique ID, this timer automatically reschedules when called again before 5 mins is up
# This works similarly to the "expire" item profile
after(5.minutes, id: Motion_Sensor) { Light_Item.off }
end
end
# timers[] is a built-in hash that keeps track of reentrant timer ids
rule 'cancel timer' do
changed Light_Item, to: OFF
run { timers[Motion_Sensor]&.cancel }
end
Or use the timed command feature to achieve the same thing
The two timer examples above required an extra rule to keep track of the Light_Item state, so when it’s turned off, the timer is cancelled. However, the timed command feature in the next example handles that automatically for you.
rule 'timed command' do
updated Motion_Sensor, to: OPEN
run { Light_Item.on for: 5.minutes } # it will turn it off after 5 minutes
end
Automatic activation of exhaust fan based on humidity sensor
This uses the evolution_rate
persistence feature, coupled with an easy way to specify duration. It is accessed simply through ItemName.persistence_function
.
# Note: don't activate the exhaust fan if the bathroom light is off at night
# Sun_Elevation is an Astro item. Its state is positive during daylight
rule 'Humidity: Control ExhaustFan' do
updated BathRoom_Humidity
triggered do |humidity|
evo_rate = humidity.evolution_rate 4.minutes, :influxdb
logger.info("#{humidity.name} #{humidity} evolution_rate: #{evo_rate}")
if (humidity > 70 && evo_rate > 15) || humidity > 85
BathRoom_ExhaustFan.ensure.on if Sun_Elevation.positive? || BathRoom_Light.state.nil? || BathRoom_Light.on?
elsif humidity < 70 || evo_rate < -5
BathRoom_ExhaustFan.ensure.off
end
end
end