Node Red: How to start / stop fan at different humidity level bathroom

I would guess that there’s no one size fits all for bath ventilation. It depends on the relative humidity of the room/house, the size of the room, and the cfm of your fan. I find that by using the derivative sensor the changes aren’t so fast that the fan regularly turns off while showering. And the humidity (represented by the derivative) seems to drop slow enough after a shower that the fan runs close to the maximum 20 minutes that I’ve set in the code as well as in the Shelly 1 auto-off timer. The only times it seems to shut off based on an actual drop in humidity is when someone just does a quick rinse instead of a full shower. I added some code that re-checks the actual humidity and if it’s above 80% I run the fan for 20 minutes, and below 80% runs it for 10 minutes. I spent way too much time trying to perfect the code and eventually called it “close enough”!

@mightybosstone Thanks for sharing the Node Red code, I was just trying to add it but came across a snag in that I cant (for some reason), install stoptimer3.I get an error saying I need to change some parameters on the install (which I cant find a way to do with NR running within Home Assistant).

Could you let me know more about what it is doing or how you have set it up and I will try and replicate it using stoptimer-varidelay if I can?

Thanks again
Neil

It’s been a while…

I’m no longer using Node Red to control my bath fans - I’ve switched to using an automation in HA using this blueprint: 🚿 Bathroom Humidity Exhaust Fan

I do still have the stoptimer3 node installed in Node Red. Not sure why it won’t install for you.

Great stuff - Thanks for the pointer - I shall use the blueprint.

Appreciate your response - thank you!

For people still looking for a relatively simple solution with NodeRED. I’m using a NodeRED State Machine and a Derivative sensor (HA Helper).

The fan is started when humidity increases with a specified rate. When humidity decreases with a specified rate, a delay is started. When humidity increases again during the delay, the delay is canceled and restarted on the next decrease. When the delay expires the fan is switched off.

image

All Inject and Debug Nodes are for demo purposes and should be removed.

[{"id":"077e718442b13a92","type":"server-state-changed","z":"fd1b36d2ab19d3e9","name":"humidity increase","server":"e6dcda3f.fd1418","version":5,"outputs":2,"exposeAsEntityConfig":"","entityId":"sensor.humidity_derived","entityIdType":"exact","outputInitially":false,"stateType":"str","ifState":"1.0","ifStateType":"num","ifStateOperator":"gt","outputOnlyOnStateChange":true,"for":"0","forType":"num","forUnits":"minutes","ignorePrevStateNull":false,"ignorePrevStateUnknown":false,"ignorePrevStateUnavailable":false,"ignoreCurrentStateUnknown":false,"ignoreCurrentStateUnavailable":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"on","valueType":"str"},{"property":"data","propertyType":"msg","value":"","valueType":"eventData"},{"property":"topic","propertyType":"msg","value":"","valueType":"triggerId"}],"x":140,"y":220,"wires":[["e42fc2824ea66779"],[]]},{"id":"d3d5423ac6a51301","type":"server-state-changed","z":"fd1b36d2ab19d3e9","name":"humidity decrease","server":"e6dcda3f.fd1418","version":5,"outputs":2,"exposeAsEntityConfig":"","entityId":"sensor.humidity_derived","entityIdType":"exact","outputInitially":false,"stateType":"str","ifState":"-0.2","ifStateType":"num","ifStateOperator":"lt","outputOnlyOnStateChange":true,"for":"0","forType":"num","forUnits":"minutes","ignorePrevStateNull":false,"ignorePrevStateUnknown":false,"ignorePrevStateUnavailable":false,"ignoreCurrentStateUnknown":false,"ignoreCurrentStateUnavailable":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"off","valueType":"str"},{"property":"data","propertyType":"msg","value":"","valueType":"eventData"},{"property":"topic","propertyType":"msg","value":"","valueType":"triggerId"}],"x":130,"y":320,"wires":[["e42fc2824ea66779"],[]]},{"id":"2039d58c03e39b24","type":"api-call-service","z":"fd1b36d2ab19d3e9","name":"Turn on","server":"e6dcda3f.fd1418","version":5,"debugenabled":false,"domain":"switch","service":"turn_on","areaId":[],"deviceId":[],"entityId":["switch.ventilatie_stand_3"],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":880,"y":100,"wires":[[]]},{"id":"658203d80989a706","type":"api-call-service","z":"fd1b36d2ab19d3e9","name":"Turn off","server":"e6dcda3f.fd1418","version":5,"debugenabled":false,"domain":"switch","service":"turn_off","areaId":[],"deviceId":[],"entityId":["switch.ventilatie_stand_3"],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":880,"y":160,"wires":[[]]},{"id":"a16d40c5ea1d6e87","type":"inject","z":"fd1b36d2ab19d3e9","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"on","payloadType":"str","x":170,"y":180,"wires":[["e42fc2824ea66779"]]},{"id":"d2c7d30608491c28","type":"debug","z":"fd1b36d2ab19d3e9","name":"debug 12","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":880,"y":260,"wires":[]},{"id":"e3af8ef41f0808e7","type":"debug","z":"fd1b36d2ab19d3e9","name":"debug 13","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":880,"y":300,"wires":[]},{"id":"e42fc2824ea66779","type":"state-machine","z":"fd1b36d2ab19d3e9","name":"","triggerProperty":"payload","triggerPropertyType":"msg","stateProperty":"payload","statePropertyType":"msg","initialDelay":"0","persistOnReload":true,"outputStateChangeOnly":true,"throwException":false,"states":["off","on","wait"],"transitions":[{"name":"on","from":"off","to":"on"},{"name":"off","from":"on","to":"wait"},{"name":"done","from":"wait","to":"off"},{"name":"on","from":"wait","to":"on"}],"x":460,"y":240,"wires":[["92474367a2347304"]]},{"id":"e843f4ccbb96cfb6","type":"inject","z":"fd1b36d2ab19d3e9","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"off","payloadType":"str","x":170,"y":280,"wires":[["e42fc2824ea66779"]]},{"id":"b6592fc0b4cd2fe9","type":"trigger","z":"fd1b36d2ab19d3e9","name":"","op1":"whatever","op2":"done","op1type":"str","op2type":"str","duration":"10","extend":true,"overrideDelay":false,"units":"min","reset":"on","bytopic":"all","topic":"topic","outputs":2,"x":510,"y":140,"wires":[[],["e42fc2824ea66779"]]},{"id":"92474367a2347304","type":"switch","z":"fd1b36d2ab19d3e9","name":"","property":"payload","propertyType":"msg","rules":[{"t":"eq","v":"wait","vt":"str"},{"t":"eq","v":"on","vt":"str"},{"t":"eq","v":"off","vt":"str"}],"checkall":"false","repair":false,"outputs":3,"x":630,"y":240,"wires":[["b6592fc0b4cd2fe9","27a65a8daa5ed4e6"],["d2c7d30608491c28","2039d58c03e39b24","b6592fc0b4cd2fe9"],["e3af8ef41f0808e7","658203d80989a706"]]},{"id":"27a65a8daa5ed4e6","type":"debug","z":"fd1b36d2ab19d3e9","name":"debug 14","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":880,"y":220,"wires":[]},{"id":"e6dcda3f.fd1418","type":"server","name":"Home Assistant","addon":true}]