For future users encountering this, I solved this by creating a sort of filter.
Simple example: my Christmas Tree. I wanted a ZigBee plug to turn on at sunset, off at sunrise, but for custom states to be retained (e.g. if someone wanted to see the lights during the day, or turn it off at night). As an extra challenge, the range of this plug to the nearest router/repeater isn’t great, so sometimes it takes a few "on"s to get it to toggle.
Both “timeswitch” and “BigTimer” from node-contrib send events over and over to indicate that the state should be on (1) or off (0). That’s why it only respects manual override for a few seconds.
What I did to solve this was use the function node with the below JS:
const COUNT = "count";
const MSG = "msg.payload";
const MAX_REPEATS = 3;
let lastMsg = flow.get(MSG);
let current = flow.get(COUNT);
if (!current || !lastMsg || (msg != lastMsg))
{
node.log("current is " + current + ", lastMsg is " + lastMsg + ", msg is " + msg + "; setting current to 0");
current = 0;
}
flow.set(MSG, msg.payload)
current++;
node.log("current incremented: " + current);
if (current >= MAX_REPEATS)
{
// discard message
node.log("discarding repeat message: " + msg.payload);
return;
}
flow.set(COUNT, current);
return msg;
And I stuck it between timeswitch (BigTimer, in your case) and the first switch mode. Now it repeats the command 3 times, then discards any future sends. I can turn the tree off/on at will, and the scheduled events will only happen (3 times) when they’re actually scheduled.
[{"id":"8cbe0f32439a2fb4","type":"group","z":"e3e0819e890caeb6","name":"Christmas Tree","style":{"stroke":"#ff0000","fill":"#c8e7a7","label":true},"nodes":["60c42fcdcab59db6","09226899caae678b","2608a78c43467dcf","d89f873ffcc7785f","09990beaadcd0c7c"],"x":14,"y":959,"w":692,"h":162},{"id":"60c42fcdcab59db6","type":"timeswitch","z":"e3e0819e890caeb6","g":"8cbe0f32439a2fb4","name":"On at sunset, Off at sunrise","mytopic":"","lat":"43.00000","lon":"-78.00000","starttime":"6000","endtime":"5000","timezone":"America/New_York","duskoff":"0","dawnoff":"-360","sun":true,"mon":true,"tue":true,"wed":true,"thu":true,"fri":true,"sat":true,"jan":true,"feb":true,"mar":true,"apr":true,"may":true,"jun":true,"jul":true,"aug":true,"sep":true,"oct":true,"nov":true,"dec":true,"x":170,"y":1000,"wires":[["09990beaadcd0c7c"]]},{"id":"09226899caae678b","type":"switch","z":"e3e0819e890caeb6","g":"8cbe0f32439a2fb4","name":"","property":"payload","propertyType":"msg","rules":[{"t":"eq","v":"1","vt":"num"},{"t":"eq","v":"0","vt":"num"}],"checkall":"true","repair":false,"outputs":2,"x":330,"y":1080,"wires":[["2608a78c43467dcf"],["d89f873ffcc7785f"]]},{"id":"2608a78c43467dcf","type":"api-call-service","z":"e3e0819e890caeb6","g":"8cbe0f32439a2fb4","name":"Turn on Christmas Tree","server":"a5439baab5b3df3f","version":5,"debugenabled":false,"domain":"switch","service":"turn_on","areaId":["family_room"],"deviceId":["0fa9357a14772f41009038b2f9128553"],"entityId":[],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":570,"y":1000,"wires":[[]]},{"id":"d89f873ffcc7785f","type":"api-call-service","z":"e3e0819e890caeb6","g":"8cbe0f32439a2fb4","name":"Turn off Christmas Tree","server":"a5439baab5b3df3f","version":5,"debugenabled":false,"domain":"switch","service":"turn_off","areaId":["family_room"],"deviceId":["0fa9357a14772f41009038b2f9128553"],"entityId":[],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":570,"y":1080,"wires":[[]]},{"id":"09990beaadcd0c7c","type":"function","z":"e3e0819e890caeb6","g":"8cbe0f32439a2fb4","name":"repeat message","func":"const COUNT = \"count\";\nconst MSG = \"msg.payload\";\nconst MAX_REPEATS = 3;\n\nlet lastMsg = flow.get(MSG);\nlet current = flow.get(COUNT);\n\nif (!current || !lastMsg || (msg != lastMsg))\n{\n node.log(\"current is \" + current + \", lastMsg is \" + lastMsg + \", msg is \" + msg + \"; setting current to 0\");\n current = 0;\n}\n\nflow.set(MSG, msg.payload)\n\ncurrent++;\n\nnode.log(\"current incremented: \" + current);\n\nif (current >= MAX_REPEATS)\n{\n // discard message\n node.log(\"discarding repeat message: \" + msg.payload);\n return;\n}\n\nflow.set(COUNT, current);\nreturn msg;","outputs":1,"noerr":0,"initialize":"// Code added here will be run once\n// whenever the node is started.\n\nflow.set(\"count\", 0);","finalize":"","libs":[],"x":140,"y":1080,"wires":[["09226899caae678b"]]},{"id":"a5439baab5b3df3f","type":"server","name":"Home Assistant","version":4,"addon":false,"rejectUnauthorizedCerts":true,"ha_boolean":"y|yes|true|on|home|open","connectionDelay":true,"cacheJson":true,"heartbeat":false,"heartbeatInterval":"30","areaSelector":"friendlyName","deviceSelector":"friendlyName","entitySelector":"friendlyName","statusSeparator":": ","statusYear":"hidden","statusMonth":"short","statusDay":"numeric","statusHourCycle":"default","statusTimeFormat":"h:m"}]
EDIT 2022-12-17: Fixed a bug in my code.