I’ve been using this to control a bath fan for about 2 years now with no issues. It uses the statistics platform to keep track of the average (mean) and standard deviation of the humidity level, and only triggers the fan once the humidity rises above the trigger percentage AND rises 5% above the previous humidity reading. It uses 2 global variables to keep track of the previous humidity level and minutes of runtime. This works well to accommodate for varying humidity levels in the house.
sensors.yaml entries for Statistics:
- platform: statistics
entity_id: sensor.master_bath_humidity_sensor_humidity
unique_id: sensor.master_bath_humidity_mean
state_characteristic: mean
name: master_bath_humidity_mean
sampling_size : 240
max_age:
hours: 48
- platform: statistics
entity_id: sensor.master_bath_humidity_sensor_humidity
unique_id: sensor.master_bath_humidity_standard_deviation
state_characteristic: standard_deviation
name: master_bath_humidity_standard_deviation
sampling_size : 240
max_age:
hours: 48
Flow:
[{"id":"07e4c320e8eb0bfb","type":"tab","label":"Master Bath Fan Control","disabled":false,"info":""},{"id":"8ef6fa0b4cbaaf89","type":"server-state-changed","z":"07e4c320e8eb0bfb","name":"","server":"8be923df.d66f9","version":4,"exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"entityidfilter":"sensor.master_bath_humidity_sensor_humidity","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":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"eventData"},{"property":"topic","propertyType":"msg","value":"","valueType":"triggerId"}],"x":245,"y":105,"wires":[["aa88e46d6cbf50a4"]]},{"id":"1645a616194978dd","type":"rbe","z":"07e4c320e8eb0bfb","name":"only pass changes","func":"rbe","gap":"5","start":"","inout":"in","property":"current","x":150,"y":165,"wires":[["4c756ec0f0161cf0"]]},{"id":"5b717be902594021","type":"api-call-service","z":"07e4c320e8eb0bfb","name":"Turn Fan On","server":"8be923df.d66f9","version":5,"debugenabled":false,"domain":"switch","service":"turn_on","areaId":[],"deviceId":[],"entityId":["switch.shelly_master_bath_fan"],"data":"","dataType":"json","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":115,"y":285,"wires":[["37eb391466852787"]]},{"id":"92796e4c9b2cad8c","type":"function","z":"07e4c320e8eb0bfb","name":"log message Fan on","func":"msg.payload=\"Fan ON for \" + msg.duration + \" min - Cur: \" + msg.current + \" Prev: \" + msg.prev + \" Avg: \" + msg.mean + \" StdDev: \" + msg.stddev + \" Trigger: \" + msg.trigger;\nreturn msg","outputs":1,"noerr":0,"initialize":"","finalize":"","x":430,"y":300,"wires":[["68585e97cd6ef104"]]},{"id":"68585e97cd6ef104","type":"api-call-service","z":"07e4c320e8eb0bfb","name":"","server":"8be923df.d66f9","version":5,"debugenabled":false,"domain":"logbook","service":"log","areaId":[],"deviceId":[],"entityId":[],"data":"{\"name\":\"Master Bath Humidity Sensor\",\"message\":\"{{payload}}\",\"entity_id\":\"sensor.master_bath_humidity_sensor_humidity\",\"domain\":\"sensor\"}","dataType":"json","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":415,"y":360,"wires":[[]]},{"id":"e2ae5a304f311291","type":"api-call-service","z":"07e4c320e8eb0bfb","name":"Turn Fan Off","server":"8be923df.d66f9","version":5,"debugenabled":false,"domain":"switch","service":"turn_off","areaId":[],"deviceId":[],"entityId":["switch.shelly_master_bath_fan"],"data":"","dataType":"json","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":100,"y":390,"wires":[["d8726de281d29653","6a7f7dabd8541ffa"]]},{"id":"0f387780b5b12a19","type":"function","z":"07e4c320e8eb0bfb","name":"log message FAN OFF & reset duration to 4 min","func":"// reset default fan duration\nglobal.set(\"var_master_bath_fan_duration\", 4);\n\n// msg.elapsed.time.minutes comes from the Hourglass node\nmsg.payload=\"Master Bath Fan OFF after \" + msg.elapsed.time.minutes + \" min - Cur: \" + msg.current + \" Avg: \" + msg.mean + \" StdDev: \" + msg.stddev + \" Trigger: \" + msg.trigger;\nreturn msg","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":285,"y":525,"wires":[["68585e97cd6ef104","dc7c4eab3a874b13"]]},{"id":"f7cde13af83e72b0","type":"comment","z":"07e4c320e8eb0bfb","name":"1 - Master Bathroom Moisture Level","info":"","x":155,"y":30,"wires":[]},{"id":"9dcb5ac5efd6aaa1","type":"inject","z":"07e4c320e8eb0bfb","name":"Report Current values","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":160,"y":600,"wires":[["aedd134b138409a0"]]},{"id":"cbfc4cc28485c70c","type":"debug","z":"07e4c320e8eb0bfb","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":520,"y":735,"wires":[]},{"id":"4e51b6c3185c3fe2","type":"function","z":"07e4c320e8eb0bfb","name":"Get average & std deviation over last 48 hours","func":"// get previous humidity reading - default to \n// current reading if not available\nvar previous_humidity = global.get(\"var_master_bath_humidity_previous\");\nvar duration = global.get(\"var_master_bath_fan_duration\")||3;\n\nvar trigger=msg.mean + (msg.stddev/2);\ntrigger=Math.round(trigger);\nmsg.payload= \"cur=\" + msg.payload + \" prev=\" + previous_humidity + \" mean=\" + msg.mean + \" stddev=\" + msg.stddev + \" trigger=\" + trigger + \" duration=\" + duration;\nreturn msg;\n\n\n\n","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":240,"y":735,"wires":[["cbfc4cc28485c70c"]]},{"id":"2b951da1e91e521c","type":"function","z":"07e4c320e8eb0bfb","name":"Determine if trigger has been reached","func":"// get previous humidity reading - default to \n// current reading if not available\nmsg.prev = global.get(\"var_master_bath_humidity_previous\"||msg.current);\nmsg.prev=Math.round(msg.prev);\nglobal.set(\"var_master_bath_humidity_previous\", msg.current);\n\nmsg.trigger=msg.mean + (msg.stddev/2)\nmsg.trigger=Math.round(msg.trigger);\n\nif (msg.current >= msg.trigger && msg.current >= (msg.prev + 5.0)) {\n\n msg.duration = global.get(\"var_master_bath_fan_duration\")||4;\n\n if (msg.duration>29) {\n // don't go over 30 minutes\n return null;\n} else {\n global.set(\"var_master_bath_fan_duration\", (msg.duration + 4));\n return msg;\n}\n\n } else {\nreturn null;\n}\n\n\n","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":465,"y":225,"wires":[["92796e4c9b2cad8c","5b717be902594021","b7804db14e183b02"]]},{"id":"b457010bc8fee662","type":"api-current-state","z":"07e4c320e8eb0bfb","name":"Get Current Humidity","server":"8be923df.d66f9","version":3,"outputs":1,"halt_if":"","halt_if_type":"num","halt_if_compare":"is","entity_id":"sensor.master_bath_humidity_sensor_humidity","state_type":"num","blockInputOverrides":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"entity"}],"for":0,"forType":"num","forUnits":"minutes","x":540,"y":680,"wires":[["4e51b6c3185c3fe2"]]},{"id":"d8726de281d29653","type":"api-current-state","z":"07e4c320e8eb0bfb","name":"Get Current Humidity","server":"8be923df.d66f9","version":3,"outputs":1,"halt_if":"","halt_if_type":"num","halt_if_compare":"is","entity_id":"sensor.master_bath_humidity_sensor_humidity","state_type":"num","blockInputOverrides":false,"outputProperties":[{"property":"current","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"entity"}],"for":0,"forType":"num","forUnits":"minutes","x":205,"y":450,"wires":[["f6c414a431bc4002"]]},{"id":"ae0b768c35f7e367","type":"inject","z":"07e4c320e8eb0bfb","name":"Reset duration to 3 min","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":175,"y":825,"wires":[["b7fb77fd982decca"]]},{"id":"b7fb77fd982decca","type":"function","z":"07e4c320e8eb0bfb","name":"Reset duration to 3 min","func":"\n global.set(\"var_master_bath_fan_duration\", 3);\n\n\n","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":410,"y":825,"wires":[[]]},{"id":"aa88e46d6cbf50a4","type":"change","z":"07e4c320e8eb0bfb","name":"Set msg.current","rules":[{"t":"set","p":"current","pt":"msg","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":600,"y":100,"wires":[["1645a616194978dd"]]},{"id":"572e2c8484aa5a88","type":"hourglass","z":"07e4c320e8eb0bfb","name":"runtime","persistId":"12345","humanizeLocale":"","x":660,"y":375,"wires":[["0f387780b5b12a19"]]},{"id":"b7804db14e183b02","type":"change","z":"07e4c320e8eb0bfb","name":"start","rules":[{"t":"set","p":"command","pt":"msg","to":"start","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":650,"y":330,"wires":[["572e2c8484aa5a88"]]},{"id":"6a7f7dabd8541ffa","type":"change","z":"07e4c320e8eb0bfb","name":"stop","rules":[{"t":"set","p":"command","pt":"msg","to":"stop","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":650,"y":435,"wires":[["572e2c8484aa5a88"]]},{"id":"f6c414a431bc4002","type":"change","z":"07e4c320e8eb0bfb","name":"status","rules":[{"t":"set","p":"command","pt":"msg","to":"status","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":650,"y":480,"wires":[["572e2c8484aa5a88"]]},{"id":"dc7c4eab3a874b13","type":"change","z":"07e4c320e8eb0bfb","name":"reset","rules":[{"t":"set","p":"command","pt":"msg","to":"reset","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":650,"y":525,"wires":[["572e2c8484aa5a88"]]},{"id":"37eb391466852787","type":"stoptimer3","z":"07e4c320e8eb0bfb","duration":"4","durationType":"num","units":"Minute","payloadtype":"num","payloadval":"0","name":"","x":110,"y":345,"wires":[["e2ae5a304f311291"],[]]},{"id":"44ce9b498d0db448","type":"inject","z":"07e4c320e8eb0bfb","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":120,"y":880,"wires":[["eac206591ef83323"]]},{"id":"f2e8224b6161890a","type":"debug","z":"07e4c320e8eb0bfb","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"stddev","targetType":"msg","statusVal":"","statusType":"auto","x":555,"y":875,"wires":[]},{"id":"eac206591ef83323","type":"api-current-state","z":"07e4c320e8eb0bfb","name":"Get stats - stddev","server":"8be923df.d66f9","version":3,"outputs":1,"halt_if":"","halt_if_type":"num","halt_if_compare":"is","entity_id":"sensor.master_bath_humidity_standard_deviation","state_type":"num","blockInputOverrides":false,"outputProperties":[{"property":"stddev","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"entity"}],"for":0,"forType":"num","forUnits":"minutes","x":310,"y":880,"wires":[["f2e8224b6161890a"]]},{"id":"4c756ec0f0161cf0","type":"api-current-state","z":"07e4c320e8eb0bfb","name":"Get stats - stddev","server":"8be923df.d66f9","version":3,"outputs":1,"halt_if":"","halt_if_type":"num","halt_if_compare":"is","entity_id":"sensor.master_bath_humidity_standard_deviation","state_type":"num","blockInputOverrides":false,"outputProperties":[{"property":"stddev","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"entity"}],"for":0,"forType":"num","forUnits":"minutes","x":390,"y":160,"wires":[["fdf56ee326a6a3d1"]]},{"id":"fdf56ee326a6a3d1","type":"api-current-state","z":"07e4c320e8eb0bfb","name":"Get stats - mean","server":"8be923df.d66f9","version":3,"outputs":1,"halt_if":"","halt_if_type":"num","halt_if_compare":"is","entity_id":"sensor.master_bath_humidity_mean","state_type":"num","blockInputOverrides":false,"outputProperties":[{"property":"mean","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"entity"}],"for":0,"forType":"num","forUnits":"minutes","x":580,"y":160,"wires":[["2b951da1e91e521c"]]},{"id":"aedd134b138409a0","type":"api-current-state","z":"07e4c320e8eb0bfb","name":"Get stats - stddev","server":"8be923df.d66f9","version":3,"outputs":1,"halt_if":"","halt_if_type":"num","halt_if_compare":"is","entity_id":"sensor.master_bath_humidity_standard_deviation","state_type":"num","blockInputOverrides":false,"outputProperties":[{"property":"stddev","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"entity"}],"for":0,"forType":"num","forUnits":"minutes","x":130,"y":660,"wires":[["3ac0985ea891f10b"]]},{"id":"3ac0985ea891f10b","type":"api-current-state","z":"07e4c320e8eb0bfb","name":"Get stats - mean","server":"8be923df.d66f9","version":3,"outputs":1,"halt_if":"","halt_if_type":"num","halt_if_compare":"is","entity_id":"sensor.master_bath_humidity_mean","state_type":"num","blockInputOverrides":false,"outputProperties":[{"property":"mean","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"entity"}],"for":0,"forType":"num","forUnits":"minutes","x":320,"y":660,"wires":[["b457010bc8fee662"]]},{"id":"8be923df.d66f9","type":"server","name":"homeassistant","version":2,"addon":true,"rejectUnauthorizedCerts":true,"ha_boolean":"","connectionDelay":false,"cacheJson":false,"heartbeat":false,"heartbeatInterval":""}]