Here is another solution that I provided in Node-RED group. The solution is very similar to the one proposed in this topic (
click here).
The requirements:
Use Node-RED to control two fans and publish the status of both fans to an MQTT broker.
Each fan should be activated for a given time (configurable) and should never be activated together. A base time should be defined (one minute or one hour for instance) and each fan should run for a slice of this base time. It should be given a break (deadtime) after fan1 is powered off and before fan2 is activated.
I tested with base time of 60 seconds, fan1 rotating for 30 seconds, fan2 rotating for 20 seconds and therefore with a dead-time (no fans rotating) of 10 seconds. The timing is fully configurable.
Proposed Solution:
It was implemented a cycling engine, inside a function node, that generates the commands to activate and deactivate each fan. The output of this function node also feeds and MQTT node that will publish the fan status.
A variable in the global context is used to store the user command to stop the flow.
The activation commands are the sent to a function node (named Test Stop) that will check if there is a pending request to stop the fans. This way the shutdown of the fans is tested each time there a new control command is created. Therefore the fan will never stop during its turn. It will first end the rotation time already commanded.
The running time for each fan is guaranteed by the delay node. Each fan has a different (configurable) timing that is propagated in a property in the msg object.
Since the control commands are the properties msg.fan1 and msg.fan2 the actual fans can be controlled by any hardware that can read these properties. It possible to control remote fans without any change in the Node-RED flow if the fan controllers are able to subscribe to the MQTT broker.
Debug in Node-RED window:
Monitoring the status in an MQTT client that subscribed to the topics:
Flow:
[{"id":"7f2e3791.ab9be8","type":"function","z":"f6b8ce66.d6b76","name":"Cycle next fan","func":"switch (msg.next) {\n \n case 1:\n msg.delay = msg.runtime1;\n msg.fan1=\"ON\"; \n msg.fan2=\"OFF\"; \n msg.next = 2;\n break;\n case 2:\n msg.delay = msg.deadtime;\n msg.fan1=\"OFF\"; \n msg.fan2=\"OFF\";\n msg.next = 3;\n break; \n case 3:\n msg.delay = msg.runtime2;\n msg.fan1=\"OFF\"; \n msg.fan2=\"ON\";\n msg.next = 1;\n break; \n \n case 4:\n msg.fan1=\"OFF\"; \n msg.fan2=\"OFF\";\n msg.reset = true;\n break; \n}\n\n\n msg1={topic:\"cmnd/fan1\", payload:msg.fan1};\n msg2={topic:\"cmnd/fan2\", payload:msg.fan2};\n\n // msg1 and msg2 are sent sequencially in outupt 1\n // msg is sent on output 2 \n return [[msg1, msg2],msg];","outputs":"2","noerr":0,"x":260,"y":220,"wires":[["92ce9449.6d90c8"],["e8228ca9.a66ce"]]},{"id":"fc7be0f.25b612","type":"inject","z":"f6b8ce66.d6b76","name":"Setup FANs","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"x":110,"y":80,"wires":[["54fbfa4f.909784"]]},{"id":"92ce9449.6d90c8","type":"mqtt out","z":"f6b8ce66.d6b76","name":"","topic":"","qos":"2","retain":"false","broker":"c7f4b651.bda738","x":530,"y":100,"wires":[]},{"id":"92b1e0bb.85b37","type":"debug","z":"f6b8ce66.d6b76","name":"","active":true,"console":"false","complete":"true","x":330,"y":380,"wires":[]},{"id":"81c129af.1fc868","type":"inject","z":"f6b8ce66.d6b76","name":"Stop FAN","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"x":160,"y":440,"wires":[["c56a07d9.6b00c8"]]},{"id":"c56a07d9.6b00c8","type":"function","z":"f6b8ce66.d6b76","name":"Command Stop","func":"global.set(\"StopFan\",true);\nreturn msg;","outputs":1,"noerr":0,"x":385.00000381469727,"y":441,"wires":[[]]},{"id":"e8228ca9.a66ce","type":"function","z":"f6b8ce66.d6b76","name":"Test Stop","func":"// user commanded stopping fans\nif (global.get(\"StopFan\")) msg.next = 4;\nreturn msg;","outputs":1,"noerr":0,"x":160,"y":340,"wires":[["9f0d2faf.3d214","92b1e0bb.85b37"]]},{"id":"54fbfa4f.909784","type":"function","z":"f6b8ce66.d6b76","name":"Initialize","func":"// Define basetime as 60 seconds (one minute)\nmsg.basetime = 60*1000;\n\n// Define runtime1 (30 seconds)\nmsg.runtime1 = 30*1000;\n\n// Define runtime2 (20 seconds)\nmsg.runtime2 = 20*1000;\n\n// Define deadtime (basetime-runtimes\nmsg.deadtime = msg.basetime-msg.runtime1-msg.runtime2;\n\n// Next fan to be activated is number 1\nmsg.next = 1;\n\n//Stop criteria is false\nglobal.set(\"StopFan\",false);\n\nreturn msg;","outputs":1,"noerr":0,"x":260,"y":80,"wires":[["7f2e3791.ab9be8"]]},{"id":"9f0d2faf.3d214","type":"delay","z":"f6b8ce66.d6b76","name":"","pauseType":"delayv","timeout":"5","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":340,"y":340,"wires":[["7f2e3791.ab9be8"]]},{"id":"c7f4b651.bda738","type":"mqtt-broker","z":"","broker":"broker.mqttdashboard.com","port":"1883","clientid":"","usetls":false,"compatmode":true,"keepalive":"60","cleansession":true,"willTopic":"","willQos":"0","willPayload":"","birthTopic":"","birthQos":"0","birthPayload":""}]