I have written a small script that remedies the situation while this is unsolved. The script listens for open/close operations that are unusually fast (e.g. below 4 seconds) and if it detects this, sends another open/close command. If the unusually fast command was in fact normal, e.g. because the covers were barely open/closed and thus the operation really only took below 4 seconds to complete than calling the additional open/close command should not have any impact.
I have tested the script with my setup and it seems to fix the situation at least temporarily for me as a hot fix.
If anyone else comes across this issue, here is my code that I used to fix this:
Code
let CONFIG = {
// minimum open/close duration that is considered normal. Faulty operations usually
// report a successful open/close operation after 2-3 seconds. This script only
// sends another set of commands to fix situation in case open/close operation took
// less than this amount of seconds
minDurationInSeconds: 4.0,
// after a faulty operation has been detected (open/close took below minDurationInSeconds),
// this script fires the commands to remedy the situation with these delays in milliseconds
debugDelaysInSeconds: [0.0, 3.0],
};
let STATES = [
{
actionTime: -1,
debugTime: -1
},
{
actionTime: -1,
debugTime: -1
}
];
// add status handler that is called once a cover starts to open or close
Shelly.addStatusHandler(function(e) {
if (e.component == null) {
// exit if component is not set
return;
}
if (e.delta.state !== "opening" && e.delta.state !== "closing") {
// exit if state is neither opening nor closing
return;
}
// get id of cover that is triggering handler (e.g. e.component = "cover:0")
let trigger = e.component.split(":");
if (trigger.length < 2 || trigger[0] != "cover") {
// exit if component that triggered handler is not a cover
return;
}
let coverId = parseInt(trigger[1]);
// get reference to global state variable of cover
let state = STATES[coverId];
print("Cover is now opening/closing");
state.actionTime = Date.now();
});
// add event handler that is called once full open or closed position has supposedly been reached
Shelly.addEventHandler(function(e) {
if (e.component == null) {
// exit if component is not set
return;
}
if (e.info.event !== "open" && e.info.event !== "closed") {
// exit if event is neither open nor closed
return;
}
// get id of cover that is triggering handler (e.g. e.component = "cover:0")
let trigger = e.component.split(":");
if (trigger.length < 2 || trigger[0] != "cover") {
// exit if component that triggered handler is not a cover
return;
}
let coverId = parseInt(trigger[1]);
// get reference to global state variable of cover
let state = STATES[coverId];
print("Cover opened/closed in " + ((Date.now() - state.actionTime)/1000).toFixed(1) + "s");
if (state.actionTime < 0 || Date.now() - state.actionTime > CONFIG.minDurationInSeconds * 1000) {
// exit if closing or opening time is above threshold, thus considered normal
return;
}
if (Date.now() - state.debugTime < (CONFIG.minDurationInSeconds + 1) * 1000) {
print("Cover opened/closed too fast, but we are in debug mode - doing nothing");
// exit if we are within an active debug time window as this means this event
// was likely triggered by our own function below - this avoids an endless loop
return;
}
print print("Cover opened/closed in under " + CONFIG.minDurationInSeconds + "s - starting debug");
// determine fix command based on reached end state
let command = e.info.event === "closed" ? "close" : "open";
let shellyCallInDebugMode = function(command, coverId) {
// start debug time window
STATES[coverId].debugTime = Date.now();
print("Now attempting to fix bugged operation - sending Shelly call...");
// make Shelly call
Shelly.call("Cover." + command, {'id': coverId});
}
// trigger delayed open/close commands to remedy the situation
for (let i=0; i<CONFIG.debugDelaysInSeconds.length; i++) {
Timer.set(
CONFIG.debugDelaysInSeconds[i] * 1000, // configure delay
false, // do not repeat timer
shellyCallInDebugMode.bind(this, command, coverId),
null
);
}
});
Alles anzeigen