I think these examples are based on a false assumption. Did you test this code?
You will never get an exception when you send a command to an Item from a rule (unless the Item doesn’t exist in the first place). So you will never get into the catch where the retry is implemented.
What you have to do is wait for a little bit (i.e. create a timer) to see if your Item changed to the expected state in response to the command and only if it didn’t to retry. This also requires the Item to have autoupdate set to false so that it doesn’t just automatically change in response to the command and instead waits for the device to report back the changed state.
It gets even more complicated for Item types where the commands and the states do not match. For example, if you send INCREASE
as a command to a Dimmer
Item, the Item’s state will never be INCREASE
, it will be an integer between 0 and 100. You can possibly infer that if the state changed at all it changed in response to the INCREASE
command. But if the Dimmer
was already at 100, it won’t change even if the command was received.
This definitely could become a design pattern but as currently implemented this code does nothing. Neither the Rules DSL nor the Jython examples will ever retry the command.
If implemented correctly, it would have somewhat limited use to cases where the device will always change state in response to every command. So Dimmers and Rollershutters and Color Items would only work most of the time.
This code is untested, but the following JS Transformation theoretically could be used in a Profile in the Item command to Channel direction to implement a retry, with the limitations discussed above. Note this code requires OHRT.
(function(data, item, delay, maxRetries) {
const {LoopingTimer} = require('openhab_rules_tools');
const timer = cache.private.get(item+'_timer', () => LoopingTimer());
const retryCommand = cache.private.get(item+'_command', () => data);
const beforeState = cache.private.get(item+'_beforeState', () => items[item].state;)
// timer is runnining, this is a retry
// pass the data through
if(!timer.hasTerminated()) {
return data;
}
// timer is running but data is different from retryCommand, this is an override
// cancel the timer
else if(!timer.hasTerminated() && data != retryCommand) {
timer.cancel();
}
// start the looping timer to retry the command
timer.loop(() => {
let numTries = cache.private.get(item+'_numTries', 0);
// Item has not changed and we haven't reached max retries
if(items[item].state == beforeState && numTries < maxRetries) {
items[item].sendCommand(data);
cache.private.put(item+'_numTries', nuTries++);
return delay;
}
// Item state finally changed or we reached max retries, exit the loop
else {
cache.private.remove(item+'_numTries');
cache.private.remove(item+'_command');
cache.private.remove(item+'_beforeState');
return null;
}
}, delay, item);
})(input, item, delay, maxRetries)
You need to pass item
, delay
(anything supported by time.toZDT()
), and maxRetries
as command line arguments using URL encoded carguments. See the docs for details. For example, if the above were added through the UI:
config:js:retry?item='MyItem'&delay='PT1S'&maxRetries=3
The above will retry the command sent to MyItem
every second up to three times.
Again, I’ve not tested this code. If I get a chance to I’ll publish it to the marketplace.