[SOLVED] Humidity based rule for SONOFF via MQTT

  • Platform information:
    • Hardware: QNAP Container
    • openHAB version: 2.4.0
  • Issue of the topic: please be detailed explaining your issue

I have setup a sonoff temperature and humidity switch. It works correctly and the humidity value is appearing in the paper GUI.

I want to set up a tule with a timer so that:

  • runs between the hours of 8am and 10pm
  • check the humidity level is abont a 70% threshold
  • turns on the machine for 20 minutes, then turns off

I have seto the rule below, but it does not work. Any ideas, please?

Thanks, George

rule "dehumidifier"
when 
//	Item sonoffth1_temperature changed
	Time cron "0,10,20,30,40,50 * 8-22 * * ?"
	
then
	if ( sonoffth1_humidity.state >= 70 ) {
		logInfo("humidity > 70")
		sonoffth1_switch.sendCommand(ON)
		createTimer( now.plusSeconds(1200), [| logInfo("Timer", "My Timer!")
		sonoffth1_switch.sendCommand(OFF)
		timer = null   // reset the timer
		])
	} 
 
end

What does not work?
What do you see in the logs?
What is the value of the humidity item?

As Vincent points out, there is an infinity of possibilities in “does not work.”

But I do see a few things here.

What’s the deal with timer = null? You don’t declare it anywhere. You don’t set it to anything. Do you even need it?

What type of Item is sonoffth1_humidity? You might need units of measure.

Use the methods on now that are more intuitive. Instead of now.plusSeconds(1200) why not now.plusMinutes(20)?

logInfo takes two arguments, a logger name and the log statement. You only provide one argument.

You should use meaningful names for your Items. If these Items represent a sonoff that controls your dehumidifer, why not use dehumidifier, dehumidifer_temp, dehumidifer_hum? Your Rules don’t care that you are commanding or getting readings from a sonoff. Why force yourself to remember which sonoff controls which device?

From a design perspective, wouldn’t it be better to have the Rule be able to trigger immediately after the humidity goes above 70 and remains on for 20 minutes?

rule "dehumidifier"
when
    Item sonoffth1_humidity changed
then
    // Ignore if it isn't the right time of day, make sure the dehumidifer is OFF
    if(now.getHourOfDay < 8 || now.getHourOfDay > 22) 
        if(sonoffth1_switch.state != OFF) sonoffth1_switch.sendCommand(OFF)
        return;

    // Ignore if the dehumidifer is already ON
    if(sonoffth1_switch.state == ON) return;

    // humidity is low
    if(sonoffth1_humidity.state < 65) {
        logInfo("humidity", "humidity is less than 65%")
        // turn off the dehumidifier? I've applied a 5% hysteresis gap
    }

    // humidity is high
    else {
        logInfo("humidity", "humidity is greater than 70%")
        sonoffth1_switch.sendCommand(ON)
        // We don't ever need to check the Timer again so we don't need to save it.
        createTimer( now.plusMinutes(20), [ |
            if(sonoffth1_switch.state != OFF) sonoffth1_switch.sendCommand(OFF)
        ])
    }
end
1 Like

Thank you very much. That code you have provided is so much more useful that what I have cribbed from other posts.

I use that and and see how I get on.

George

I think you have a TH10/16 Sonoff with Tasmota-Flash and no UoM ?! How do your .items and your .things look like ?
Did you try to find out if your rule triggers at all ? To do so just insert a logInfo in the execution Block, like this:

rule "dehumidifier"
when 
//	Item sonoffth1_temperature changed
	Time cron "0,10,20,30,40,50 * 8-22 * * ?"
	
then
     logInfo("dehumidifier","Rule triggers")   // Rule triggers
	if ( sonoffth1_humidity.state >= 70 ) {
		logInfo("humidity > 70")
		sonoffth1_switch.sendCommand(ON)
		createTimer( now.plusSeconds(1200), [| logInfo("Timer", "My Timer!")
		sonoffth1_switch.sendCommand(OFF)
		timer = null   // reset the timer
		])
	} 
 
end

If you get the Triggerinformation in your Logger, your If-Clause doesn’t work as expected. Otherwise your Trigger is not correct.
So the better way is what Rich suggested.

// humidity is high
    else {
        logInfo("humidity", "humidity is greater than 70%")
        sonoffth1_switch.sendCommand(ON)
        // We don't ever need to check the Timer again so we don't need to save it.
        createTimer( now.plusMinutes(20), [ |
            if(sonoffth1_switch.state != OFF) sonoffth1_switch.sendCommand(OFF)
        ])
    }

The ELSE path is reached at 66% r.h. …
You should do an

else if(sonoffth1_humidity.state > 69) {

in this part to get the hysteresis…

Thank you everyone. I have tweaked and adjusted the code, but I cannot get the timer to trigger. I will post the final code when I have finished and it works. .

The toimer section does not seem to create a 5 minute timer, instead is seems to be about 1 minute.

Have I defined the timer section correctly?

Is there a binding to install for the timer?


    // humidity is high
    else {
        logInfo("humidity", "humidity is greater than 80%. timer on")
        sonoffth1_switch.sendCommand(ON)
        // We don't ever need to check the Timer again so we don't need to save it.
        createTimer( now.plusMinutes( 5 ), [ | 
            if (sonoffth1_switch.state != OFF) {
				sonoffth1_switch.sendCommand(OFF)
				logInfo("humidity", "timer off")
			}
        ])
    }
2019-09-27 10:49:50.475 [INFO ] [ipse.smarthome.model.script.humidity] - gckhour = 10ON64.7
2019-09-27 10:49:50.476 [INFO ] [ipse.smarthome.model.script.humidity] - already on
2019-09-27 10:50:00.003 [INFO ] [ipse.smarthome.model.script.humidity] - gckhour = 10ON64.7
2019-09-27 10:50:00.014 [INFO ] [ipse.smarthome.model.script.humidity] - already on
2019-09-27 10:50:00.006 [ERROR] [org.quartz.core.JobRunShell         ] - Job DEFAULT.2019-09-27T10:50:00.005+01:00: Proxy for org.eclipse.xtext.xbase.lib.Procedures$Procedure0: [ | {
  org.eclipse.xtext.xbase.impl.XIfExpressionImpl@6a9f537f
} ] threw an unhandled Exception: 
java.lang.NullPointerException: null
	at org.eclipse.smarthome.model.script.engine.ScriptError.<init>(ScriptError.java:66) ~[?:?]
	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.invokeFeature(ScriptInterpreter.java:140) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:902) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:865) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:224) ~[?:?]
	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:226) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:204) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:768) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:220) ~[?:?]
	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:226) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:204) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.evaluateArgumentExpressions(XbaseInterpreter.java:1116) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._invokeFeature(XbaseInterpreter.java:1046) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.invokeFeature(XbaseInterpreter.java:992) ~[?:?]
	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.invokeFeature(ScriptInterpreter.java:151) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:902) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:226) ~[?:?]
	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:226) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:204) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:458) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:244) ~[?:?]
	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:226) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:204) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:447) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:228) ~[?:?]
	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:226) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:204) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.evaluate(XbaseInterpreter.java:190) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.ClosureInvocationHandler.doInvoke(ClosureInvocationHandler.java:46) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.AbstractClosureInvocationHandler.invoke(AbstractClosureInvocationHandler.java:29) ~[?:?]
	at com.sun.proxy.$Proxy167.apply(Unknown Source) ~[?:?]
	at org.eclipse.smarthome.model.script.internal.actions.TimerExecutionJob.execute(TimerExecutionJob.java:49) ~[?:?]
	at org.quartz.core.JobRunShell.run(JobRunShell.java:202) [107:org.eclipse.smarthome.core.scheduler:0.10.0.oh240]
	at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:573) [107:org.eclipse.smarthome.core.scheduler:0.10.0.oh240]
2019-09-27 10:50:00.032 [ERROR] [org.quartz.core.ErrorLogger         ] - Job (DEFAULT.2019-09-27T10:50:00.005+01:00: Proxy for org.eclipse.xtext.xbase.lib.Procedures$Procedure0: [ | {
  org.eclipse.xtext.xbase.impl.XIfExpressionImpl@6a9f537f
} ] threw an exception.
org.quartz.SchedulerException: Job threw an unhandled exception.
	at org.quartz.core.JobRunShell.run(JobRunShell.java:213) [107:org.eclipse.smarthome.core.scheduler:0.10.0.oh240]
	at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:573) [107:org.eclipse.smarthome.core.scheduler:0.10.0.oh240]
Caused by: java.lang.NullPointerException
	at org.eclipse.smarthome.model.script.engine.ScriptError.<init>(ScriptError.java:66) ~[?:?]
	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.invokeFeature(ScriptInterpreter.java:140) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:902) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:865) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:224) ~[?:?]
	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:226) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:204) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:768) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:220) ~[?:?]
	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:226) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:204) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.evaluateArgumentExpressions(XbaseInterpreter.java:1116) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._invokeFeature(XbaseInterpreter.java:1046) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.invokeFeature(XbaseInterpreter.java:992) ~[?:?]
	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.invokeFeature(ScriptInterpreter.java:151) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:902) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:226) ~[?:?]
	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:226) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:204) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:458) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:244) ~[?:?]
	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:226) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:204) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:447) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:228) ~[?:?]
	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:226) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:204) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.evaluate(XbaseInterpreter.java:190) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.ClosureInvocationHandler.doInvoke(ClosureInvocationHandler.java:46) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.AbstractClosureInvocationHandler.invoke(AbstractClosureInvocationHandler.java:29) ~[?:?]
	at com.sun.proxy.$Proxy167.apply(Unknown Source) ~[?:?]
	at org.eclipse.smarthome.model.script.internal.actions.TimerExecutionJob.execute(TimerExecutionJob.java:49) ~[?:?]
	at org.quartz.core.JobRunShell.run(JobRunShell.java:202) ~[?:?]
	... 1 more
2019-09-27 10:50:00.368 [WARN ] [l.generic.ChannelStateTransformation] - Executing the JSONPATH-transformation failed: Invalid path '$.POWER' in 'OFF'
2019-09-27 10:50:10.002 [INFO ] [ipse.smarthome.model.script.humidity] - gckhour = 10OFF64.7
2 Likes

Thank you. I restarted openhab. This stopped the errors appearing, but the timer still only runs for a very, very short time.

OMG. I have just spend the past ages trying to figure out what was wrong. Only to discover that I had left an EXPIRE on the item!!!

I think it will work now…

Here is my final working code. Thank you everyone!

rule "dehumidifier"
when
    //Item sonoffth1_humidity changed
	Time cron "0,10,20,30,40,50 * * * * ?"
then
	val gckhour =  now.getHourOfDay
    logInfo("humidity", "gckhour = " + gckhour + sonoffth1_switch.state 
	        + sonoffth1_humidity.state )

	// Ignore if it isn't the right time of day, make sure the dehumidifer is OFF
    if( gckhour < 8 || gckhour > 22) {
		logInfo("humidity", "outside of hours "+ gckhour )
		if(sonoffth1_switch.state != OFF) {
		//	sonoffth1_switch.sendCommand(OFF)
		}	
        return;
	}
    // Ignore if the dehumidifer is already ON
    else if(sonoffth1_switch.state == ON) {
		logInfo("humidity", "already on")
		return;
	}
    // humidity is low
    else if(sonoffth1_humidity.state < 75) {
        logInfo("humidity", "humidity is less than 90%")
        // turn off the dehumidifier? I've applied a 5% hysteresis gap
		return;
    }

    // humidity is high
    else {
        logInfo("humidity", "humidity is greater than 80%. timer on")
        
        // We don't ever need to check the Timer again so we don't need to save it.
        if (sonoffth1_switch.state == OFF) {
			logInfo("humidity", "timer on")
			sonoffth1_switch.sendCommand(ON)
			createTimer( now.plusMinutes(20), [ | 
				logInfo("humidity", "timer off")
				sonoffth1_switch.sendCommand(OFF)
        	])
		}
    }

end