[SOLVED] OpenSprinkler Irrigation and Cascading timers rule

I can’t say what is wrong. This code was written for OH 2.5ish, maybe even OH 1.8. A lot has changed in the six years since this was written.

Today, the best approach would be to use the JS Scripting add-on with OHRT using the Gatekeeper. If coding isn’t your thing Blockly is a great choice and there’s a Block Library to access OHRT from Blockly.

Create Switch Items for each valve and Number Items for each Time you want the valve to run. If you use Number:Time, make sure to set the unit to s.

“Reset Irrigation at OH Start” is no longer needed.

For “Start Irrigation at 08:00” you’ll create a rule that triggers same as the above except we want to trigger on all commands to Irrigation_Manual.

I personally would replace the cron expression with a Time is Item trigger and put the 08:00 into a DateTime Item so you can easily reschedule the irrigation start time from another rule or the UI or use iCal or Time State Machine or Timeline Picker to schedule the start.

In Blockly the code for the action would be something like this:

image

Put all the valves into a Group:Switch.

I don’t have suitable Items to show in this example.

In the if block that runs when Irrigation_Manual receives command OFF, you want to send command to the Group Item in place of “MyItem”.

In the else block you’ll replace the MyItem in the call to Gatekeeper with the Number Item that holds the duration the valve should remain open. If it’s a Number:Time Item, make sure to use s as the unit.

The first call to Gatekeeper turns on the first valve so replace “MyItem” with your first valve’s Switch Item. The subsequent calls to Gatekeeper turn off the previous valve and turn on the next ones.

The final call to Gatekeeper just turns off the last valve. Replace all the "MyItem"s as approrpriate.

“Irrigation Cascade”, and “Cancel Irrigation” are no lonfer needed.

The above code in JS would look something like the following. In this case you should use Number:Time Items for the durations but now it doesn’t matter what unit you set the Item to.

var {Gatekeeper} = require('openhab_rules_tools');

if ((event !== undefined)  && event.type == 'ItemCommandEvent' && event.itemCommand?.toString() == 'OFF') {
  cache.private.get('gk')?.cancelAll();
  items.AllValves.sendCommand('OFF');
} 
else {
  const gatekeeper = cache.private.get('gk', () => Gatekeeper());
  gatekeeper.cancelAll();
  items.AllValves.sendCommand('OFF');

  gatekeeper.addCommand(items.Valve1Duration, () =>  items.Valve1.sendCommand('ON') );

  gatekeeper.addCommand(items.Valve2Duration, () => {
    items.Valve1.sendCommand('OFF');
    items.Valve2.sendCommand('ON');
  });
  
  gatekeeper.addCommand(items.Valve3Duration, () => {
    items.Valve2.sendCommand('OFF');
    items.Valve3.sendCommand('ON');
  });

  gatekeeper.addCommand(0, () =>  items.Valve3.sendCommand('OFF') );
}

You could get clever and loop through members of a Group to reduce the code even more but left it like this to make it more clear how this code works. Even without the loop there are ways to improve this code to reduce the amount of duplication. But I’ve left the code like this to make it easier to follow.

This approach is way easier to understand, maintain, and it way less brittle compared to the above.

The way both work is the rule triggers either based on a command the the ‘Manual_Irrigation’ Item or time. If it was triggered by an OFF command to Manual_Irrigation, all timers are cancelled and OFF is sent to all valve Items.

In all other triggers the irrigation starts to be scheduled. First we cancel anything that might already be running to handle the rare cases where irrigation is already running and Manual_Irrigation was commanded ON again. The alternative is to ignore the command which we can do by setting and checking a flag or something like that. If using JS code you can see if the Gatekeeper timer is scheduled (there isn’t a block for that yet).

Then we schedule the ON and OFF commands to the various valves using Gatekeeper. The Gatekeeper will run the functions in the order they are added, and then wait the passed in duration before running the next.