Weird, I must have copied wrong. It’s supposed to be the full YAML. I’ll get that fixed as soon as I get to a computer. It’s stuff like this that I filed an issue to have a file import/export instead of copy and paste.
No, state
is correct. When I first wrote the Python version where I defined the metadata it only supported one state. I’ve since expanded to to support more than one state but to avoid breaking things I kept the metadata unchanged. You can see on line 38 that I pull the metadata using “state”, not “states”.
Therefore, the error message on line 21 is correct. The metadata should use “state” not “states”.
Actually it should be end_debounce_generator(arg, arg, arg)()
. Clearly I didn’t test the case where the state isn’t debounced. 
Didn’t work how? The function is written in that way on purpose to solve a problem with scope. end_debounce_generator returns a function()
because the Timer expects a fucntion()
. But if you create an anonymous function (e.g. function() { end_debounce(arg1, arg2, arg3); }
), arg1, arg2, and arg3 do not get fixed to be the values they had at the time the anonymous function was created. Instead they will have the values the last time the rule ran. So I call a function to create the function()
so that the argument values get fixed and remain the values that they were when the function was generated so they don’t get overwritten by later runs of the rule.
I’m not surprised as some of your fixes were incorrect fixes.
Try the following YAML:
triggers:
- id: "1"
configuration:
groupName: Debounce
type: core.GroupStateChangeTrigger
conditions: []
actions:
- inputs: {}
id: "2"
configuration:
type: application/javascript
script: >
var logger =
Java.type("org.slf4j.LoggerFactory").getLogger("org.openhab.model.script.Rules.Debounce");
// Get Metadata query stuff
this.FrameworkUtil = (this.FrameworkUtil === undefined) ? Java.type("org.osgi.framework.FrameworkUtil") : this.FrameworkUtil;
this._bundle = (this._bundle === undefined) ? FrameworkUtil.getBundle(scriptExtension.class) : this._bundle;
this.bundle_context = (this.bundle_context === undefined) ? this._bundle.getBundleContext() : this.bundle_context;
this.MetadataRegistry_Ref = (this.MetadataRegistry_Ref === undefined) ? bundle_context.getServiceReference("org.openhab.core.items.MetadataRegistry") : this.MetadataRegistry_Ref;
this.MetadataRegistry = (this.MetadataRegistry === undefined) ? bundle_context.getService(MetadataRegistry_Ref) : this.MetadataRegistry;
this.Metadata = (this.Metadata === undefined) ? Java.type("org.openhab.core.items.Metadata") : this.Metadata;
this.MetadataKey = (this.MetadataKey === undefined) ? Java.type("org.openhab.core.items.MetadataKey") : this.MetadataKey;
// Load TimerMgr
this.OPENHAB_CONF = (this.OPENHAB_CONF === undefined) ? java.lang.System.getenv("OPENHAB_CONF") : this.OPENHAB_CONF;
load(this.OPENHAB_CONF+'/automation/lib/javascript/community/timerMgr.js');
/**
* Get and check the item metadata.
* @return {dict} The metadata parsed and validated
*/
var checkMetadata = function(itemName) {
var USAGE = "Debounce metadata should follow debounce=ProxyItem[command=true, timeout='2s', state='ON,OFF']."
var cfg = MetadataRegistry.get(new MetadataKey("debounce", itemName));
if(cfg === null) {
throw itemName + " does not have debounce metadata! " + USAGE;
}
if(cfg.value === undefined || cfg.value === null) {
throw itemName + " does not have a proxy Item defined! " + USAGE;
}
if(cfg.configuration["timeout"] == undefined || cfg.configuration["timeout"] === null) {
throw itemName + " does not have a timeout parameter defined! " + USAGE;
}
var dict = {"proxy": cfg.value,
"timeout": cfg.configuration["timeout"],
"command": "command" in cfg.configuration && cfg.configuration["command"].toLowerCase() == "true",
};
var stateStr = cfg.configuration["state"];
if(stateStr === undefined || stateStr === null) {
throw itemName + " does not have proper debounce metadata " + cfg.toSTring;
}
var split = stateStr.split(",");
dict["states"] = [];
for(st in split) {
dict["states"].push(split[st]);
}
return dict;
}
/**
* Called when the debounce timer expires, transfers the current state to the
* proxy Item.
* @param {string} state the state to transfer to the proxy Item
* @param {string} name of the proxy Item
* @param {Boolean} when true, the state is sent as a command
*/
var end_debounce_generator = function(state, proxy, isCommand) {
return function() {
logger.info("End debounce for " + proxy + " new state = " + state + " curr state = " + items[proxy]);
if(isCommand && items[proxy] != state) {
events.sendCommand(proxy, state);
}
else if (items[proxy].toString != state) {
events.postUpdate(proxy, state);
}
}
}
this.timers = (this.timers === undefined) ? new TimerMgr() : this.timers;
var cfg = checkMetadata(event.itemName);
if(cfg["states"].length == 0 ||
(cfg["states"].length > 0 && cfg["states"].indexOf(event.itemState.toString()) >= 0)) {
logger.info("Debouncing " + event.itemName + " with proxy = " + cfg["proxy"]
+ " timeout = " + cfg["timeout"] + " and states = " + cfg["states"]);
this.timers.check(event.itemName, cfg["timeout"],
end_debounce_generator(event.itemState, cfg["proxy"], cfg["command"]));
}
else {
logger.info(event.itemName + " changed to " + event.itemState + " which is not debouncing");
end_debounce_generator(event.itemState, cfg["proxy"], cfg["command"])();
}
type: script.ScriptAction
Note, I’ve moved the logging to info instead of debug for now. I did a couple of tests including testing the not/debounced state and it seems to work.
But if you used “states” in your metadata, you’ll need to change them to “state”.