Humidity Automation using a Timer

I want to share my solution for regulating humidity indoors.

Concept
I do not like the dry air indoors in winter. So I got myself a humidifier:

It’s a finde device with a couple of glitches though:

  1. It does not turn on/off automatically
  2. It does not measure Humidity
  3. The water tank only lasts for about 11h, then you need to refill water.

We can see, this thing desperately needs automation

Automation of the Humidifier

I installed an indoor Temperature sensors (Aqara Temp/Hum Sensor). Every 30min it broadcasts temp/hum.

Number Sensor2_Humidity     "Humidity Livingroom [%d %%]"                      (fg_gAqara,gGF_gHumidity)                 {mqtt="<[MQTT:mqtt/zigbee2mqtt/Aqara_TempSensor2:state:JSONPATH($.humidity)]"}

With the help of a smart plug of your choosing it is quite sraight forward to turn on the Humidifier, when Humidity is below 37% and turn it off again when it is above 42% (whatever values you deem necessary).

Note: I took out all logInfos but I highly recommend using them when making rules, they immensly helped me to see where the problem is. Especially with timers.

rule "Humidifier on/off"
when 
Item Sensor2_Humidity changed

then
val Number humidityHigh = 42
val Number humidityLow = 37
val Number humidityCurrent = Sensor2_Humidity.state as DecimalType

  	if (humidityCurrent <= humidityLow) { LivingRoom_Humidifier_Power.sendCommand(ON) 
            }
     else if (humidityCurrent >= humidityHigh) { LivingRoom_Humidifier_Power.sendCommand(OFF) 
            }
end

I did not implement threshholds, because the update interval is 30min, so min runtime of the Humidifier will be 30min which I can live with.

So 1 and 2 are already solved. Let’s get to #3

Make sure that the Humidifier does not run when empty

I had to remind myself that openhab is event driven.
I added a couple of extra items:

Number LivingRoom_Humidifier_TimeRemain "Remaining Time to refill [%d]"
Switch LivingRoom_Humidifier_Empty "Empty"  

The basic idea is that the Switch _Empty is turned to on, when TimeRemain=0
Additionaly above rule needs a slight modification, as we only want it to trigger when the tank is not empty.

So just add:

if (LivingRoom_Humidifier_Empty.state==OFF){
//the rest of the rule goes here
} 

Creating a runime Counter
Now let’s configure a timer to determine then the Empty Tank event actually occurs:

The idea:
whenever the Humidifier is turned on, take the remaining time that is left until the tank is empty.
If the remaining runtime reaches 0 - turn off the humidifier and notify that it’s empty.

var Timer remainingTimeCounter=null

rule "Remaining"
when 
Item LivingRoom_Humidifier_Power changed to ON 
then 
var remainingTime = LivingRoom_Humidifier_TimeRemain.state as Number
remainingTimeCounter = createTimer(now.plusMinutes(1), [|
if(remainingTime > 0){
    remainingTime = remainingTime - 1
    LivingRoom_Humidifier_TimeRemain.postUpdate(remainingTime)
    }
if(remainingTime <= 0 ) {
    LivingRoom_Humidifier_Empty.sendCommand(ON)
    LivingRoom_Humidifier_Power.sendCommand(OFF)
    }
remainingTimeCounter.reschedule(now.plusMinutes(1))
    ])
end

So every Minute the Item LivingRoom_Humidifier_TimeRemain is updated, then the timer is rescheduled. If the Item LivingRoom_Humidifier_TimeRemain equals 0 the Humidifier will be turned off.

Note: By separation the On/OFF rule and the Remaining Time Counter we make sure, that the remaining time also updates when the Humidifier is turned on manually.

Now we need a rule that cancels the timer, when the humidifier is turned off (be it by hand or automatically)

rule "cancel Timer when Humidifier is turned off"
when
System started or
Item LivingRoom_Humidifier_Power changed to OFF
then
remainingTimeCounter?.cancel
remainingTimeCounter = null
end

Finally we need a rule that resets the remaining time to 11h, when the tank is refilled.

rule "Humidifier refill is done"
when
Item LivingRoom_Humidifier_Empty changed to OFF
then
remainingTimeCounter?.cancel
remainingTimeCounter = null
LivingRoom_Humidifier_Empty.sendCommand(OFF)
LivingRoom_Humidifier_TimeRemain.postUpdate(660)
end

There you go. For convenience one might want to add a proxy item for Refill. That way you can refill the tank (thus resetting the remainTime to 11h) even if the tank was not empty.

4 Likes

Very nice,:+1: thanks for sharing.

And of course, if you guys have tips to improve the code feel free - I am not very good at coding :slight_smile:

I’m not the best at coding either,:smile: but the important thing is the code works and you understand why it works.:wink:

Your code is clean.
I only would make sure that the indenting is in order. It makes it easier to read:
Also check the counter is not null before cancelling it or you may get errors
And check the timer doesn’t already exist before creating it:

var Timer remainingTimeCounter=null

rule "Remaining"
when 
    Item LivingRoom_Humidifier_Power changed to ON 
then 
    var remainingTime = LivingRoom_Humidifier_TimeRemain.state as Number
    if (remainingTimerCounter !== null) return; //Do nothing timer already exist
    remainingTimeCounter = createTimer(now.plusMinutes(1), [ |
        if (remainingTime > 0) {
            remainingTime = remainingTime - 1
            LivingRoom_Humidifier_TimeRemain.postUpdate(remainingTime)
        }
        if (remainingTime <= 0 ) {
            LivingRoom_Humidifier_Empty.sendCommand(ON)
            LivingRoom_Humidifier_Power.sendCommand(OFF)
        }
        remainingTimeCounter.reschedule(now.plusMinutes(1))
    ])
end

rule "cancel Timer when Humidifier is turned off"
when
    System started or
    Item LivingRoom_Humidifier_Power changed to OFF
then
    if (remainingTimeCounter !== null) {
        remainingTimeCounter?.cancel
        remainingTimeCounter = null
    }
end

rule "Humidifier refill is done"
when
    Item LivingRoom_Humidifier_Empty changed to OFF
then
    if (remainingTimeCounter !== null) {
        remainingTimeCounter?.cancel
        remainingTimeCounter = null
        LivingRoom_Humidifier_Empty.sendCommand(OFF)
        LivingRoom_Humidifier_TimeRemain.postUpdate(660)
    }
end