Ok, so I have created a proxy item with the same name as what I’ve written in the raw sensor metadata, put that new item inside the debounce group. But this proxy item doesn’t reflect the open or closed state… So I can’t use it to create a notification rule…
I’m totally lost. I don’t know which code to post here to show you where I’m wrong… A bit upset.
Here is the metadata in the raw sensor
value: Garage_Door_Sensor
config:
timeout: 1s
Here is my debounce rule
configuration:
debounceGroup: Debounce
triggers:
- id: "1"
configuration:
groupName: Debounce
type: core.GroupStateChangeTrigger
conditions:
- inputs: {}
id: "2"
label: Only debounce non NULL and UNDEF states
configuration:
type: application/javascript
script: >-
if(typeof(require) === "function") Object.assign(this,
require('@runtime'));
var type = (typeof(require) === "function") ? UnDefType : UnDefType.class;
event.itemState.class != type;
type: script.ScriptCondition
actions:
- inputs: {}
id: "3"
label: Debounce state based on debounce metadata
description: "Expected Metadata: debounce=ProxyItem[timeout='2s',
state='OFF', command=true]"
configuration:
type: application/javascript
script: >
if(typeof(require) === "function") Object.assign(this,
require('@runtime'));
var logger = Java.type("org.slf4j.LoggerFactory").getLogger("org.openhab.model.script.Rules.rules_tools.Debounce");
// Get Metadata query stuff
this.FrameworkUtil = (this.FrameworkUtil === undefined) ? Java.type("org.osgi.framework.FrameworkUtil") : this.FrameworkUtil;
this.ScriptHandler = Java.type("org.openhab.core.automation.module.script.rulesupport.shared.ScriptedHandler");
this._bundle = (this._bundle === undefined) ? FrameworkUtil.getBundle(ScriptHandler.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
// TODO: Replace with an installed library when the marketplace supports it
//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');
var TimerMgr = function() {
var OPENHAB_CONF = java.lang.System.getenv("OPENHAB_CONF");
this.log = Java.type("org.slf4j.LoggerFactory").getLogger("org.openhab.model.script.Rules.rules_tools.TimerMgr");
this.log.debug("Building timerMgr instance.");
this.timers = {};
// this.log.debug("Loading timeUtils");
// load(OPENHAB_CONF+'/automation/lib/javascript/community/timeUtils.js');
this.ScriptExecution = Java.type("org.openhab.core.model.script.actions.ScriptExecution");
this.log.debug("Timer Mgr is ready to operate");
}
// reproduced from timeUtils, truncated to only parse duration strings
TimerMgr.prototype.toDateTime = function(when) {
var Duration = Java.type("java.time.Duration");
var ZonedDateTime = Java.type("java.time.ZonedDateTime");
var dur = null;
var dt = null;
var regex = new RegExp(/[\d]+[d|h|m|s|z]/gi);
var numMatches = 0;
var part = null;
var params = { "d": 0, "h": 0, "m":0, "s":0, "z":0 };
while(null != (part=regex.exec(when))) {
this.log.debug("Match = " + part[0]);
numMatches++;
var scale = part[0].slice(-1).toLowerCase();
var value = Number(part[0].slice(0, part[0].length-1));
params[scale] = value;
}
if(numMatches === 0){
this.log.warn("Could not parse any time information from '" + timeStr +"'. Examples of valid string: '8h', '2d8h5s200z', '3d 7m'.");
}
else {
this.log.debug("Days = " + params["d"] + " hours = " + params["h"] + " minutes = " + params["m"] + " seconds = " + params["s"] + " msec = " + params["z"]);
dur = Duration.ofDays(params["d"]).plusHours(params["h"]).plusMinutes(params["m"]).plusSeconds(params["s"]).plusMillis(params["z"]);
}
if(dur !== null) {
dt = ZonedDateTime.now().plus(dur);
}
return dt;
},
TimerMgr.prototype._notFlapping = function(key) {
this.log.debug("Timer expired for " + key);
if (key in this.timers && "notFlapping" in this.timers[key]) {
this.log.debug("Calling expired function " + this.timers[key]["notFlapping"]);
this.timers[key]["notFlapping"]();
}
if (key in this.timers){
this.log.debug("Deleting the expired timer");
delete this.timers[key];
}
},
TimerMgr.prototype._noop = function() { },
TimerMgr.prototype.check = function(key, when, func, reschedule, flappingFunc) {
this.log.debug("Timer manager check called");
if (reschedule === undefined) reschedule = false;
var timeout = this.toDateTime(when);
this.log.debug("Timer to be set for " + timeout.toString());
// Timer exists
if (key in this.timers){
if (reschedule){
this.log.debug("Rescheduling timer " + key + " for " + timeout.toString());
this.timers[key]["timer"].reschedule(timeout);
}
else {
this.log.debug("Cancelling timer " + key);
this.cancel(key);
}
if (flappingFunc !== undefined){
this.log.debug("Running flapping function for " + key);
flappingFunc();
}
}
// Timer doesn't already exist, create one
else {
this.log.debug("Creating timer for " + key);
var timer = this.ScriptExecution.createTimerWithArgument(timeout, this, function(context) { context._notFlapping(key); });
this.timers[key] = { "timer": timer,
"flapping": flappingFunc,
"notFlapping": (func !== undefined) ? func : this._noop }
this.log.debug("Timer created for " + key);
}
},
TimerMgr.prototype.hasTimer = function(key) {
return key in this.timers;
},
TimerMgr.prototype.cancel = function(key) {
if (key in this.timers) {
this.timers[key]["timer"].cancel();
delete this.timers[key];
}
},
TimerMgr.prototype.cancelAll = function() {
for (var key in this.timers) {
if (!this.timers[key]["timer"].hasTerminated()) {
this.log.debug("Timer has not terminated, cancelling timer " + key);
this.cancel(key);
}
delete this.timers[key];
this.log.debug("Timer entry has been deleted for " + key);
}
}
// TODO: End
/**
* Get and check the item metadata.
* @return {dict} The metadata parsed and validated
*/
var checkMetadata = function(itemName, timers) {
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(items[cfg.value === undefined]) {
throw "Proxy Item " + cfg.value + " does not exist! " + USAGE;
}
if(cfg.configuration["timeout"] == undefined || cfg.configuration["timeout"] === null) {
throw itemName + " does not have a timeout parameter defined! " + USAGE;
}
if(timers.toDateTime(cfg.configuration["timeout"]) === null) {
throw itemName + "'s timeout " + cfg.configuration["timeout"] + " is not valid! " + USAGE;
}
var dict = {"proxy": cfg.value,
"timeout": cfg.configuration["timeout"],
"command": "command" in cfg.configuration && cfg.configuration["command"].toString().toLowerCase() == "true",
};
dict["states"] = [];
var stateStr = cfg.configuration["states"];
if(stateStr !== undefined && stateStr !== null) {
var split = stateStr.split(",");
for(var 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.debug("End debounce for " + proxy + ", new state = " + state + ", curr state = " + items[proxy] + ", command = " + isCommand);
if(isCommand && items[proxy] != state) {
logger.debug("Sending command " + state + " to " + proxy);
events.sendCommand(proxy, state.toString());
}
else if (items[proxy] != state) {
logger.debug("Posting update " + state + " to " + proxy);
events.postUpdate(proxy, state.toString());
}
}
}
this.timers = (this.timers === undefined) ? new TimerMgr() : this.timers;
var cfg = checkMetadata(event.itemName, this.timers);
if(cfg["states"].length == 0 ||
(cfg["states"].length > 0 && cfg["states"].indexOf(event.itemState.toString()) >= 0)) {
logger.debug("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.debug(event.itemName + " changed to " + event.itemState + " which is not debouncing");
this.timers.cancel(event.itemName); // Cancel the timer if it exists
end_debounce_generator(event.itemState, cfg["proxy"], cfg["command"])();
}
type: script.ScriptAction
Now, the proxy sensor is supposed to show open or closed, I presume. It’s just NULL.