Using JRuby with Scene Switches to control items

I have a bunch of inovelli dimmer switches and each switch has multiple scenes depending on how many times you tap up or down. Instead of making individual rules for all of these I wrote one rule in jruby. It turned out to be super handy so I thought I’d share it. It should work with basically any type of openhab item state combo, not just switches. I know something similar could be done with python or javascript, but I thought the ruby approach was just so much cleaner. If you haven’t tried the JRuby Scripting addon I definitely recommend giving it a shot.

require 'openhab'

UPx1        = "2.0"
UPRELEASE   = "2.1"
UPHOLD      = "2.2"
UPx2        = "2.3"
UPx3        = "2.4"
UPx4        = "2.5"
DOWNx1      = "1.0"
DOWNRELEASE = "1.1"
DOWNHOLD    = "1.2"
DOWNx2      = "1.3"
DOWNx3      = "1.4"
DOWNx4      = "1.5"

class Scene
  @@scenes = Hash.new{|h, k| h[k] = {}}

  def initialize
    @switch_statuses = Hash.new{|h, k| h[k] = {}}
    @item_commands = {}
  end

  def switch_status(switch, status)
    @switch_statuses[switch.name][status.to_s] ||= {}
    self
  end

  def item_command(item, command)
    @item_commands[item.name] = command
    self
  end
  
  def add
    @switch_statuses.each do |switch, statuses|
      statuses.each_key{|status| @@scenes[switch][status] = @item_commands }
    end
  end

  def self.switches
    @@scenes.keys.map{|k| items[k]}
  end

  def self.send_commands(switch)
    item_command_hash = @@scenes[switch.name][switch.state.to_s] || return

    item_command_hash.each_pair do |i, command|
      if items.include? i
        item = items[i]
        logger.info("Send Item: #{i} Command: #{command}")
        if item.is_a?(Group)
          item.group << command
        else
          item << command
        end
      else
        logger.info("Item #{i} not found!")
      end
    end

  end
end
# Define your Scenes here. I've given a few examples.
Scene.new.switch_status(FamilyRoom_Dimmer_1_SceneNumber, UPx1)
         .switch_status(FamilyRoom_Dimmer_2_SceneNumber, UPx1)
         .item_command(FamilyRoom_Lights_Color, "ON").add

Scene.new.switch_status(FamilyRoom_Dimmer_1_SceneNumber, UPx2)
         .switch_status(FamilyRoom_Dimmer_2_SceneNumber, UPx2)
         .item_command(FamilyRoom_Lights_Color, "100").add

Scene.new.switch_status(FamilyRoom_Dimmer_1_SceneNumber, DOWNx1)
         .switch_status(FamilyRoom_Dimmer_2_SceneNumber, DOWNx1)
         .item_command(FamilyRoom_Lights_Color, "OFF").add         

Scene.new.switch_status(FamilyRoom_Dimmer_1_SceneNumber, DOWNx2)
         .switch_status(FamilyRoom_Dimmer_2_SceneNumber, DOWNx2)
         .item_command(FamilyRoom_Lights_Color, "30").add

Scene.new.switch_status(FamilyRoom_Dimmer_1_SceneNumber, DOWNx3)
         .item_command(FamilyRoom_Projector_Power, "ON")
         .item_command(FamilyRoom_Curtain_BlindsControl, "DOWN").add  

Scene.new.switch_status(FamilyRoom_Projector_Power, "OFF")
         .item_command(FamilyRoom_Curtain_BlindsControl, "UP").add 

rule 'Switch Scenes' do
  changed Scene.switches
  triggered do |switch|
    Scene.send_commands(switch)
  end
end

I created a Scene class has 3 main methods.
switch_status which takes an item and state. This is the item you want to watch and the state you are trying to use. eg: switch_status(MySwitch_1_SceneNumber, “1.3”). Which would trigger when I tap my switch down twice.
item_command takes the item that you are trying to control and the command you want to give it. eg: item_command(FamilyRoom_Lights,“ON”)
add is placed at the end of the method chain.

Both switch_status and item_command can be chained multiple times to add more switches or items to control for the scene. I also defined a few constants at the top so I didn’t have to remember what state value went to what amount of switch taps.