Hint: I’ve already solved the challenge, but here it is in case you’re interested.
Note: I utilized global variables in NodeRed to store boolean values rather than creating input_booleans in HA.
Goal
The goal was to smarten up the ceiling fans to turn on and off when certain conditions were met, and to provide a way to manually override the automation without using the HA interface to switch something on or off.
Here’s how I did it:
Fan Season
Should the ceiling fans even be on? What’s the criteria? In our house, we set a rule that says that any time the outside temperature (Dark Sky) falls below a specific input number value, the ceiling fans should shut down. We can set that threshold temperature any number of ways, but for now, it’s done using an input_number from the HA front end with no initial value set.
In NodeRed, a simple 60 second inject sends a pulse through a flow to check those very conditions and ultimately shuts the fans down.
So what does the first condition look like? A switch. The switch checks the property global.homeassistant.homeAssistant.states['sensor.dark_sky_temperature'].state
to see if it is less than global.homeassistant.homeAssistant.states['input_number.ceiling_fan_temp'].state
If so, the flow continues. If not, nothing happens.
Establish and Disable Manual Override
At midnight, every night, an inject node triggers a function node. In that function, I store false
values in a global variable object called status
for each fan. So each fan is now in automation mode with manual override turned off based upon the values of the global variable.
Setting the Manual Override to True
Any time the state of a single fan changes, a switch node checks to see if the context value for user_id in the msg.payload for that fan is null. If it is, a change node changes the value of the global variable to true, placing that fan in manual override mode. As long as the fan remains in this mode (which expires at midnight) the 60 second Fan Season inject will be ignored by that fan.
Any time someone presses one of the Zwave wall switches to turn on or turn off a fan, the fan goes into manual override mode based upon the user_id context.
It uses Dark Sky for outside temperature, and it uses the Link node to connect flows from one tab to the next (that’s an organizational thing.)
Here is the ceiling fan flow:
[{"id":"fb358d1b.ddca3","type":"link in","z":"9fa967ab.9b4a38","name":"","links":["8b36054a.2cd968"],"x":195,"y":820,"wires":[["b8d87f5f.4432e"]]},{"id":"9b432c97.b2fcc","type":"comment","z":"9fa967ab.9b4a38","name":"Turn off All Fans when Outside Temp is Less than Ceiling Fan Temp","info":"","x":420,"y":780,"wires":[]},{"id":"b8d87f5f.4432e","type":"switch","z":"9fa967ab.9b4a38","name":"Not Fan Weather","property":"homeassistant.homeAssistant.states['sensor.dark_sky_temperature'].state","propertyType":"global","rules":[{"t":"lt","v":"homeassistant.homeAssistant.states['input_number.ceiling_fan_temp'].state","vt":"global"}],"checkall":"true","repair":false,"outputs":1,"x":330,"y":820,"wires":[["36c704e.da201fc","fe73c86f.d54a68","81b2729a.f865e","1195de54.c226d2"]]},{"id":"774d01ed.2cc1c","type":"server-state-changed","z":"9fa967ab.9b4a38","name":"","server":"c7cbc76b.fd06a8","version":1,"entityidfilter":"fan.kitchen","entityidfiltertype":"exact","outputinitially":false,"state_type":"str","haltifstate":"on","halt_if_type":"str","halt_if_compare":"is","outputs":2,"output_only_on_state_change":true,"x":290,"y":1000,"wires":[["8d874499.f4af88"],[]]},{"id":"8d874499.f4af88","type":"switch","z":"9fa967ab.9b4a38","name":"Manual Action?","property":"data.new_state.context.user_id","propertyType":"msg","rules":[{"t":"null"}],"checkall":"true","repair":false,"outputs":1,"x":560,"y":1000,"wires":[["35ac81c6.bce87e"]]},{"id":"35ac81c6.bce87e","type":"change","z":"9fa967ab.9b4a38","name":"Enable Manual Override","rules":[{"t":"set","p":"status.fanKitchenManualOverride","pt":"global","to":"true","tot":"bool"}],"action":"","property":"","from":"","to":"","reg":false,"x":790,"y":1000,"wires":[[]]},{"id":"36c704e.da201fc","type":"switch","z":"9fa967ab.9b4a38","name":"Fan Kitchen","property":"status.fanKitchenManualOverride","propertyType":"global","rules":[{"t":"false"}],"checkall":"true","repair":false,"outputs":1,"x":750,"y":820,"wires":[["23944393.8ee72c"]]},{"id":"fe73c86f.d54a68","type":"switch","z":"9fa967ab.9b4a38","name":"Fan Living Room","property":"status.fanLivingRoomManualOverride","propertyType":"global","rules":[{"t":"false"}],"checkall":"true","repair":false,"outputs":1,"x":770,"y":860,"wires":[["b56fe05e.8115"]]},{"id":"81b2729a.f865e","type":"switch","z":"9fa967ab.9b4a38","name":"Fan Master Bedroom","property":"status.fanMasterBedroomManualOverride","propertyType":"global","rules":[{"t":"false"}],"checkall":"true","repair":false,"outputs":1,"x":780,"y":900,"wires":[["8170fb12.000228"]]},{"id":"1195de54.c226d2","type":"switch","z":"9fa967ab.9b4a38","name":"Fan Guest Bedroom","property":"status.fanGuestBedroomManualOverride","propertyType":"global","rules":[{"t":"false"}],"checkall":"true","repair":false,"outputs":1,"x":780,"y":940,"wires":[["67f5b7e8.b0be88"]]},{"id":"e59c7d1b.dd1f1","type":"comment","z":"9fa967ab.9b4a38","name":"Manual Override?","info":"","x":770,"y":780,"wires":[]},{"id":"91cb1ad2.063538","type":"server-state-changed","z":"9fa967ab.9b4a38","name":"","server":"c7cbc76b.fd06a8","version":1,"entityidfilter":"fan.living_room","entityidfiltertype":"exact","outputinitially":false,"state_type":"str","haltifstate":"on","halt_if_type":"str","halt_if_compare":"is","outputs":2,"output_only_on_state_change":true,"x":310,"y":1040,"wires":[["ec2c8d61.31d8"],[]]},{"id":"ec2c8d61.31d8","type":"switch","z":"9fa967ab.9b4a38","name":"Manual Action?","property":"data.new_state.context.user_id","propertyType":"msg","rules":[{"t":"null"}],"checkall":"true","repair":false,"outputs":1,"x":560,"y":1040,"wires":[["e662b8f2.690308"]]},{"id":"e662b8f2.690308","type":"change","z":"9fa967ab.9b4a38","name":"Enable Manual Override","rules":[{"t":"set","p":"status.fanLivingRoomManualOverride","pt":"global","to":"true","tot":"bool"}],"action":"","property":"","from":"","to":"","reg":false,"x":790,"y":1040,"wires":[[]]},{"id":"a52a3454.a14618","type":"change","z":"9fa967ab.9b4a38","name":"Enable Manual Override","rules":[{"t":"set","p":"status.fanGuestBedroomManualOverride","pt":"global","to":"true","tot":"bool"}],"action":"","property":"","from":"","to":"","reg":false,"x":790,"y":1080,"wires":[[]]},{"id":"212d735.dcf148c","type":"switch","z":"9fa967ab.9b4a38","name":"Manual Action?","property":"data.new_state.context.user_id","propertyType":"msg","rules":[{"t":"null"}],"checkall":"true","repair":false,"outputs":1,"x":560,"y":1080,"wires":[["a52a3454.a14618"]]},{"id":"3d7cef35.4b42a","type":"server-state-changed","z":"9fa967ab.9b4a38","name":"","server":"c7cbc76b.fd06a8","version":1,"entityidfilter":"fan.guest_bedroom","entityidfiltertype":"exact","outputinitially":false,"state_type":"str","haltifstate":"on","halt_if_type":"str","halt_if_compare":"is","outputs":2,"output_only_on_state_change":true,"x":320,"y":1080,"wires":[["212d735.dcf148c"],[]]},{"id":"fed44983.760828","type":"change","z":"9fa967ab.9b4a38","name":"Enable Manual Override","rules":[{"t":"set","p":"status.fanMasterBedroomManualOverride","pt":"global","to":"true","tot":"bool"}],"action":"","property":"","from":"","to":"","reg":false,"x":790,"y":1120,"wires":[[]]},{"id":"89006895.c85f28","type":"switch","z":"9fa967ab.9b4a38","name":"Manual Action?","property":"data.new_state.context.user_id","propertyType":"msg","rules":[{"t":"null"}],"checkall":"true","repair":false,"outputs":1,"x":560,"y":1120,"wires":[["fed44983.760828"]]},{"id":"a5a1a1a5.45d55","type":"server-state-changed","z":"9fa967ab.9b4a38","name":"","server":"c7cbc76b.fd06a8","version":1,"entityidfilter":"fan.master_bedroom","entityidfiltertype":"exact","outputinitially":false,"state_type":"str","haltifstate":"on","halt_if_type":"str","halt_if_compare":"is","outputs":2,"output_only_on_state_change":true,"x":320,"y":1120,"wires":[["89006895.c85f28"],[]]},{"id":"23944393.8ee72c","type":"api-call-service","z":"9fa967ab.9b4a38","name":"Kitchen Fan Off","server":"c7cbc76b.fd06a8","version":1,"service_domain":"fan","service":"turn_off","entityId":"fan.kitchen","data":"","dataType":"json","mergecontext":"","output_location":"","output_location_type":"none","mustacheAltTags":false,"x":1000,"y":820,"wires":[[]]},{"id":"b56fe05e.8115","type":"api-call-service","z":"9fa967ab.9b4a38","name":"Living Room Fan Off","server":"c7cbc76b.fd06a8","version":1,"service_domain":"fan","service":"turn_off","entityId":"fan.living_room","data":"","dataType":"json","mergecontext":"","output_location":"","output_location_type":"none","mustacheAltTags":false,"x":1020,"y":860,"wires":[[]]},{"id":"67f5b7e8.b0be88","type":"api-call-service","z":"9fa967ab.9b4a38","name":"Guest Bedroom Fan Off","server":"c7cbc76b.fd06a8","version":1,"service_domain":"fan","service":"turn_off","entityId":"fan.guest_bedroom","data":"","dataType":"json","mergecontext":"","output_location":"","output_location_type":"none","mustacheAltTags":false,"x":1030,"y":940,"wires":[[]]},{"id":"8170fb12.000228","type":"api-call-service","z":"9fa967ab.9b4a38","name":"Master Bedroom Fan Off","server":"c7cbc76b.fd06a8","version":1,"service_domain":"fan","service":"turn_off","entityId":"fan.master_bedroom","data":"","dataType":"json","mergecontext":"","output_location":"","output_location_type":"none","mustacheAltTags":false,"x":1030,"y":900,"wires":[[]]},{"id":"c7cbc76b.fd06a8","type":"server","z":"","name":"Home Assistant","legacy":false,"hassio":true,"rejectUnauthorizedCerts":true,"ha_boolean":"y|yes|true|on|home|open","connectionDelay":true}]
And the function used to establish manual override global variable:
var status = {
"fanKitchenManualOverride" : false,
"fanLivingRoomManualOverride" : false,
"fanGuestBedroomManualOverride" : false,
"fanMasterBedroomManualOverride" : false
};
global.set("status", status);
return msg;
And another function to store all of HA states and services in a global variable:
global.set("states", global.get('homeassistant').homeAssistant.states);
global.set("services", global.get('homeassistant').homeAssistant.services);
If anyone wants me to make a screen capture of this setup, let me know and I’ll throw it up on YouTube.