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

image

you can create a sensor to use as a condition/trigger or plainly use this in your flow, whatever… This way you’ll distinguish whether it’s rising or dropping.

You want to turn the ventilator on at 70% and then off at 60%? I don’t quite get your explanation…

Thanks Mightybosstone, I could’ve figured that out myself. Let’s see if this works!

Obaldius: I want to turn the fan on at a lower value (e.g. 60), so that it turns on quickly. Then I want it to turn off at a higher value (e.g. 70), to prevent it from running very long.

Make sure you’ve set up the statistics on your humidity sensor long enough beforehand (several days) so that it can have a good value for the mean and std deviation. The beauty of this approach is that you don’t need to care about the actual humidity level (60%, 70%, etc). You’re only concerned with having it trigger when it raises above the norm, and it only adds run-time while the humidity is increasing and stops adding minutes as soon as the humidity drops.

1 Like

FYI, there is a new helper introduced (in the latest HA release 2022.4.5) that measures the derivative (dx/dt). i.e. it measure the rate of change of a measurement with respect to time. Since humidity fluctuates depending on the time of the year (relatively slowly), measuring on the derivative provides large fluctuations in humidity for the sensor being measured and therefore can provide a trigger for your fan to turn ‘on’ and subsequently 'off ’ as the derivative turns negative.

Helpers can be found ‘Configuration’ → ‘Automations & Scenes’ → ‘Helpers’ → ’ Add Helper’ → ‘Derivative Helper’

1 Like

Thanks, I made a derivative sensor, let’s see how that works out.

I had this flow disabled as we were on holiday, just enabled it. Let’s see how this works out.

The master_bath_humidity_mean and -standard_deviation are Unavailable though, should I just wait or did I something wrong?

Did you set up your 2 - platform: statistics in sensors.yaml and restart? One for the mean and one for the standard deviation.

Thanks bastero for pointing out the new derivative integration! I build a new flow based on it and it seems to be working just as well as using the mean/std deviation method, with much less code and complexity. I’ll post that code in a bit.

1 Like

Here’s an alternate flow to control the bath fan based on the new derivative integration that bastero mentioned. I’ve been testing it by just logging the start and stop times, and it seems to work as expected.

For the derivative sensor I used a time window of 00:00:00 and a time unit of ‘minutes’. In theory it should only trigger when the derivative value rises or drops below zero, but I found that it sometimes would bounce to a value of positive 1, so I set the switch to turn on when the value is >=2.

[{"id":"fcb3c47492902266","type":"tab","label":"Bath Fan Control","disabled":false,"info":"","env":[]},{"id":"2ad8bfddd3944e76","type":"server-state-changed","z":"fcb3c47492902266","name":"Master Bath Humidity","server":"8be923df.d66f9","version":4,"exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"entityidfilter":"sensor.master_bath_humidity_testa","entityidfiltertype":"exact","outputinitially":false,"state_type":"num","haltifstate":"","halt_if_type":"num","halt_if_compare":"is","outputs":1,"output_only_on_state_change":true,"for":0,"forType":"num","forUnits":"minutes","ignorePrevStateNull":true,"ignorePrevStateUnknown":true,"ignorePrevStateUnavailable":true,"ignoreCurrentStateUnknown":true,"ignoreCurrentStateUnavailable":true,"outputProperties":[{"property":"humidity","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"eventData"},{"property":"topic","propertyType":"msg","value":"","valueType":"triggerId"}],"x":160,"y":100,"wires":[["975a3edb4b829a46"]]},{"id":"975a3edb4b829a46","type":"switch","z":"fcb3c47492902266","name":"+ or -","property":"humidity","propertyType":"msg","rules":[{"t":"gte","v":"2","vt":"num"},{"t":"lte","v":"0","vt":"num"}],"checkall":"false","repair":false,"outputs":2,"x":330,"y":100,"wires":[["5f1f208fe0d412e2"],["e23d2b9b09892744"]]},{"id":"5f1f208fe0d412e2","type":"function","z":"fcb3c47492902266","name":"log message Fan on","func":"var Time = new Date();\nmsg.payload=\"Fan ON \" +  msg.humidity + \"%\";\n\nflow.set(\"var_master_bath_fan_start_time\", Time);\n   \nreturn msg","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":540,"y":100,"wires":[["640cce28efe28e3b"]]},{"id":"62f095e952acf380","type":"function","z":"fcb3c47492902266","name":"log message Fan off","func":"var Time = new Date();\nvar stop_time = flow.get(\"var_master_bath_fan_start_time\");\nvar duration =(Time - stop_time)/1000;\nduration=Math.ceil(duration*100)/100;\nduration=Math.ceil(duration/60);\nmsg.payload=\"Fan OFF \" +  msg.humidity + \"% \" + duration + \" min\";\nreturn msg","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":540,"y":280,"wires":[["640cce28efe28e3b"]]},{"id":"640cce28efe28e3b","type":"api-call-service","z":"fcb3c47492902266","name":"","server":"8be923df.d66f9","version":5,"debugenabled":false,"domain":"logbook","service":"log","areaId":[],"deviceId":[],"entityId":[],"data":"{\"name\":\"Master Bath Humidity Sensor TEST\",\"message\":\"{{payload}}\",\"entity_id\":\"sensor.master_bath_humidity_sensor_humidity\",\"domain\":\"sensor\"}","dataType":"json","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":970,"y":160,"wires":[[]]},{"id":"e23d2b9b09892744","type":"api-current-state","z":"fcb3c47492902266","name":"Get Fan Status","server":"8be923df.d66f9","version":3,"outputs":1,"halt_if":"","halt_if_type":"str","halt_if_compare":"is","entity_id":"switch.shelly_master_bath_fan","state_type":"str","blockInputOverrides":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"entity"}],"for":"0","forType":"num","forUnits":"minutes","override_topic":false,"state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","x":520,"y":160,"wires":[["982ed164b76de56b"]]},{"id":"982ed164b76de56b","type":"switch","z":"fcb3c47492902266","name":"Fan On or Off?","property":"payload","propertyType":"msg","rules":[{"t":"eq","v":"on","vt":"str"},{"t":"eq","v":"off","vt":"str"}],"checkall":"true","repair":false,"outputs":2,"x":520,"y":220,"wires":[["62f095e952acf380"],[]]},{"id":"24047622b15b3d4a","type":"comment","z":"fcb3c47492902266","name":"Turn Fan switch ON Here","info":"","x":810,"y":120,"wires":[]},{"id":"3fdb0c9275dbd467","type":"comment","z":"fcb3c47492902266","name":"Turn Fan switch OFF Here","info":"","x":770,"y":220,"wires":[]},{"id":"8be923df.d66f9","type":"server","name":"homeassistant","version":2,"addon":true,"rejectUnauthorizedCerts":true,"ha_boolean":"","connectionDelay":false,"cacheJson":false,"heartbeat":false,"heartbeatInterval":""}]
3 Likes

Hi. Where in your flow are you specifying the derivative value? I guess if you can let me know what you called it i can find it.

Create an entity/helper in Home Assistant: Settings - Devices and Services - Helpers - Create Helper.

I’m getting an error when deploying the flow. It is:

The workspace contains some nodes that are not properly configured:

  • [Bath Fan Control] log message Fan off (function)

Not sure where to start since I haven’t played with logs before. I really appreciate any help you can provide.

One of the updates to Node Red caused me to change the way I was getting the current time so I could calculate the duration. Change the following in each of the functions and see if that works:

Change from:

var Time = new Date();

To:

var Time = new Date().valueOf();

Also change the variable name stop_time to start_time at both locations in the second function as that is actually the time the fan was started and the flow variable populated.

That cleared out the errors on deployment, thanks! I’ll keep running it. Thanks for sharing the flow!

I’m somewhat confused by this. You’re actually reading the rate of change of the humidity to trigger the automation, right? When I run the fan, the humidity immediately starts dropping rapidly, even with the shower running, and if I understand this logic correctly it will shut off immediately.

Also, wouldn’t this automation shut off the fan if, for example, someone turned it on manually, if the humidity was dropping for any reason at all? I’m going to run this through some trials shortly, but I must admit some confusion. I feel like the state machine needs to be a little more complicated for this to work. Are you running it in production yet? Or still testing?

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}]