OH3 rule for Toggle Switch to trigger a script: Is there a more elegant solution than mine? (v2)

Hi all,

A toggle switch is turning two lights on / off. All is configured within the OH3-UI. It works stable. But the rule that triggers the script looks “awful”. Do you have a more elegant solution?

Thanks for any hints.

Simon

The hardware
A Toggle Switch is connected to a fibaro ZWAVE dimmer (S2). This THING is only used to deliver the position of the Toggle Switch. The fibaro ZWAVE dimmer has the CHANNEL Scene Number. It provides these numbers based on the interactions with the Toggle Switch: 20 (position 1), 21 (position 2), 24 (fast double push), 25 (fast triple push) and null.

The rule
The rule needs to track when the Scene Number changes from one number to another.

All simpler approaches lead into uncontrolled switching of the light (e.g. switching the light whenever openhab restarts.):

  • A simple “update” of the state of the Scene Number, or
  • A trigger like “null” to 21

configuration: {}
triggers:
  - id: "1"
    configuration:
      itemName: Bedroom_Scene
      state: "21"
      previousState: "20"
    type: core.ItemStateChangeTrigger
  - id: "2"
    configuration:
      itemName: Bedroom_Scene
      state: "21"
      previousState: "24"
    type: core.ItemStateChangeTrigger
  - id: "3"
    configuration:
      itemName: Bedroom_Scene
      state: "21"
      previousState: "25"
    type: core.ItemStateChangeTrigger
  - id: "4"
    configuration:
      itemName: Bedroom_Scene
      state: "20"
      previousState: "21"
    type: core.ItemStateChangeTrigger
  - id: "5"
    configuration:
      itemName: Bedroom_Scene
      state: "20"
      previousState: "24"
    type: core.ItemStateChangeTrigger
  - id: "6"
    configuration:
      itemName: Bedroom_Scene
      state: "20"
      previousState: "25"
    type: core.ItemStateChangeTrigger
  - id: "7"
    configuration:
      itemName: Bedroom_Scene
      state: "24"
      previousState: "20"
    type: core.ItemStateChangeTrigger
  - id: "8"
    configuration:
      itemName: Bedroom_Scene
      state: "24"
      previousState: "21"
    type: core.ItemStateChangeTrigger
  - id: "9"
    configuration:
      itemName: Bedroom_Scene
      state: "24"
      previousState: "25"
    type: core.ItemStateChangeTrigger
  - id: "10"
    configuration:
      itemName: Bedroom_Scene
      state: "25"
      previousState: "20"
    type: core.ItemStateChangeTrigger
  - id: "11"
    configuration:
      itemName: Bedroom_Scene
      state: "25"
      previousState: "21"
    type: core.ItemStateChangeTrigger
  - id: "12"
    configuration:
      itemName: Bedroom_Scene
      state: "25"
      previousState: "24"
    type: core.ItemStateChangeTrigger
conditions: []
actions:
  - inputs: {}
    id: "20"
    configuration:
      considerConditions: true
      ruleUIDs:
        - Spare_LightScript
    type: core.RunRuleAction

The script

var Sensor = 'Bedroom_Scene';

var ItemADimmer             = 'SpareCeiling_LightDimmer';
var ItemATemperature        = 'SpareCeiling_LightTemperature';
var ItemBDimmer             = 'SpareTable_LightDimmer';
var ItemBTemperature        = 'SpareTable_LightTemperature';
  
var BrightItemADimmer       = 100;
var BrightItemATemperature  = 60;
var BrightItemBDimmer       = 80;
var BrightItemBTemperature  = 50;

var CosyItemADimmer         = 10;
var CosyItemATemperature    = 100;
var CosyItemBDimmer         = 40;
var CosyItemBTemperature    = 100;

if (itemRegistry.getItem(Sensor).getState() == 21 || itemRegistry.getItem(Sensor).getState() == 20) {;
    if (itemRegistry.getItem(ItemADimmer).getState() == 0 && itemRegistry.getItem(ItemBDimmer).getState() == 0) {;
        events.sendCommand(ItemADimmer, ON);
        events.sendCommand(ItemBDimmer, ON);
    } else {;
        events.sendCommand(ItemADimmer, OFF);
        events.sendCommand(ItemBDimmer, OFF);  
    };
};
	
if (itemRegistry.getItem(Sensor).getState() == 24) {;
    events.sendCommand(ItemADimmer, BrightItemADimmer);
    events.sendCommand(ItemATemperature, BrightItemATemperature);                                                    
    events.sendCommand(ItemBDimmer, BrightItemBDimmer);
    events.sendCommand(ItemBTemperature, BrightItemATemperature);                                                    
};

if (itemRegistry.getItem(Sensor).getState() == 25) {;
    events.sendCommand(ItemADimmer, CosyItemADimmer);
    events.sendCommand(ItemATemperature, CosyItemATemperature);                                                    
    events.sendCommand(ItemBDimmer, CosyItemBDimmer);
    events.sendCommand(ItemBTemperature, CosyItemATemperature);                                                    
};

The Environment

  • Platform information:
    • Hardware: Raspberry Pi 4 8GB
    • OS: ubuntu server 22.04
    • openHAB version: Docker with official image: openhab/openhab:3.2.0

The “from state” and “to state” sections of the trigger dialog are optional. You just want just one “changed” without any specifier. Then your rule will trigger not on every update from the device (that’s “item updated”) but only when an update from the device causes the item state to change. Since you explicitly test for the all specific states in the rule script that you care about, it shouldn’t really matter what the state changes to or changes from.

Hi Justin

Thanks for your reply.

It seems not to work without the “from state” and “to state” sections as shown bellow.

configuration: {}
triggers:
  - id: "1"
    configuration:
      itemName: Bedroom_Scene
    type: core.ItemStateChangeTrigger
conditions: []
actions:
  - id: "2"
    configuration:
      ruleUIDs:
        - SpareLight_Switch
    type: core.RunRuleAction

The next example works in principle. But unfortunately OH updates the state on several occasions, even if the toggle switch is not pushed (e.g. a restart of OH).

configuration: {}
triggers:
  - id: "1"
    configuration:
      itemName: Bedroom_Scene
    type: core.ItemStateUpdateTrigger
conditions: []
actions:
  - id: "2"
    configuration:
      ruleUIDs:
        - SpareLight_Switch
    type: core.RunRuleAction

There is no reason why the rule won’t run without the from state and to state sections. In fact, it should run more often (and just not produce any results based on your script); the presence of those parts of the rule definition pare down the possible triggers. Any change from, for example, 21 to 24 is also just a change.

If you are not seeing the results you expect when you just use the bare item changed trigger, then there is something else going on:

  • Do you see any errors in the logs when this rule runs?
  • Add some logging to the top of your rule so you know for sure it is being called or not. Perhaps the rule is running and just not executing the statements you expect.
  • When you test it with the item changed trigger, check your log file. Make sure the log says Item Bedroom_Scene changed from X to Y If there’s no such log line then there’s no event to trigger the rule.

Your recommended trigger works in principle (I don’t know what I messed up in the first attempt). Unfortunately the initial problem persists, which triggers this rule whenever openhab restarts. This is because this scene changes from “NULL” to whichever state the item has. You find this also in the “event.logs”:

2022-04-27 13:34:03.375 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'Bedroom_Scene' changed from NULL to 20

What might work is a trigger that excludes an item change “from state” NULL.

Do you know how I had to write this?

I found a simpler way to build the trigger, since I’m aware that the “state” and “previousState” is optional. Even more elegant would be a solution that excludes / filters “previousState” that are NULL. Is this possible after all? Could you help me with the syntax?

Thanks in advance.

Simon

configuration: {}
triggers:
  - id: "1"
    configuration:
      itemName: Bedroom_Scene
      previousState: "20"
    type: core.ItemStateChangeTrigger
  - id: "2"
    configuration:
      itemName: Bedroom_Scene
      previousState: "21"
    type: core.ItemStateChangeTrigger
  - id: "3"
    configuration:
      itemName: Bedroom_Scene
      previousState: "24"
    type: core.ItemStateChangeTrigger
  - id: "4"
    configuration:
      itemName: Bedroom_Scene
      previousState: "25"
    type: core.ItemStateChangeTrigger
conditions: []
actions:
  - inputs: {}
    id: "5"
    configuration:
      considerConditions: true
      ruleUIDs:
        - Spare_LightScript
    type: core.RunRuleAction

This problem can be dealt with independent of the rule if you want. This is exactly what the the persistence feature of Restore On Startup is for. You can check out persistence setup and restoreOnStartup in the persistence docs (I personally use mapDB for this sort of thing).

Yes, handling the NULL state within the rule is the other perfectly viable option and there are two easy ways to do this.

Option 1: This is exactly what the rule conditions are for in the UI rules (the But only if section). There you can add a simple script. The last line of the script must evaluate to true or false and if true the rule will run and if false the will will not run. So here you could a script (assuming the built-in javascript option) that says:

(event.oldItemState != 'NULL')

The entire condition would look like this:

conditions:
  - inputs: {}
    id: "4"
    configuration:
      type: application/javascript
      script: (event.oldItemState != 'NULL')
    type: script.ScriptCondition

Option 2: Just add a similar check to the top of your rule already existing rule script.

1 Like

Works like a charm. Thanks a lot.