[SOLVED] Rule not working (cron triggered DHT22 value dependent 433MHz Switches)

I’m new to this and still fairly vague about the concepts.

My Setup is: a DHT22 measuring Humidity (and Temp) via a python script, and a Water pump whose powerplug is controlled by the humidity level.

The sensor is setup as number items updating by rules via command line (no associated Thing. Is that bad practice? see my setup below).

The Switch for the pump can be controlled manually, and it also works ‘automatically’ if the humidity value is above threshold; the pump turns off.

The Problem is this: when I add that rule, the sensor stops updating and just stays stuck at the last value. What am I doing wrong?

things:

    Thing   exec:command:aqualight-control "Aqualight Control Command" [command="/home/openhabian/aquabeet/aqualight_switch.sh %2$s", interval=0, timeout=10, autorun=true]                                                                                                
    Thing   exec:command:aquapump-control "Aquapump Control Command" [command="/home/openhabian/aquabeet/aquapump_switch.sh %2$s", interval=0, timeout=10, autorun=true]

items:

Number  tmp_dht22                                       "DHT22 Temp [%.1f °C]"
Number  humidity_dht22                                  "DHT22 Humidity [%.1f %%]"
Number  tmp_cpu                                         "DHT22 CPUTemp [%.1f °C]"
Number  volts_pi                                        "DHT22 PiVolts [%.1f V]"
String AqualightSwitch "Aqualight" <light> ["Switchable"] { channel="exec:command:aqualight-control:input", autoupdate="true" }
String AquapumpSwitch "Aquapump" <humidity> ["Switchable"] { channel="exec:command:aquapump-control:input", autoupdate="true" }

rules (working, without the automatic pump control:

rule "DHT22"
when
        Time cron "3 */1 * * * ?"
then
        val TEMP = executeCommandLine("/home/openhabian/dht22/read_temperature.py", 10000)
        Thread::sleep(11000)
        if (TEMP.toString().length <= 5) tmp_dht22.postUpdate(TEMP)
        Thread::sleep(400)
        val HUMID = executeCommandLine("/home/openhabian/dht22/read_humidity.py", 10000)
        Thread::sleep(11000)
        if (HUMID.toString().length <= 5) humidity_dht22.postUpdate(HUMID)
        Thread::sleep(400)
end

The offending pump control rule that ‘stalls’/blocks the sensor from updating, if I delete this the sensor goes back to updating (I add this before the ‘end’ of the previous rule):

        if(humidity_dht22.state as DecimalType < 60) AquapumpSwitch.sendCommand("ON")
        Thread::sleep(400)
        if(humidity_dht22.state as DecimalType > 65) AquapumpSwitch.sendCommand("OFF")

another rule for turning on Light by time works fine:

rule "AutoAquaLighton"
when
        Time cron "13 0 7 * * ?"
then
        AqualightSwitch.sendCommand("ON")
end

rule "AutoAquaLightoff"
when
        Time cron "13 0 20 * * ?"
then
        AqualightSwitch.sendCommand("OFF")
end

and this is my sitemap, where I can switch on and off both the pump and the light without problems:

sitemap demo label="My home automation" {
   Frame label="Aquabeet" {
        Text item=tmp_dht22 label="DHT22 Temp [%.1f °C]"
        Text item=humidity_dht22 label="DHT22 Humidity [%.1f %%]"
	Switch item=AqualightSwitch mappings=[ "ON"="ON", "OFF"="OFF" ]
        Switch item=AquapumpSwitch mappings=[ "ON"="ON", "OFF"="OFF" ]
        }
}

First question: Did you do a reload on the UI? or did you encounter no changes also on the item (via Events.log, …)
Second questions:

  • What does your python script return? you’re sure it’s the right string? do you have your system configured, that the openHAB user can execute?
  • are there any entries in the logs?
    third question:
  • what’s the purpose of your rule, with all those thread::sleeps and all? You start the rule every Minute and three seconds and repeat with a 0,4sec pause in between - I don’t get the logic here! :wink:

last question:
why don’t you either:

  • tell your python-script to update the value directly (via REST API or MQTT)
  • let the OH-rule directly put the output of the python-script write in the variable? (and make sure, your python script does all the mapping and error avoiding?)

First Question: I reloaded (and graphed). Shamefully, I had not taken a look into the log files yet (just trying and changing until things work from tutorials). Apparently my rule is wrong, but I can’t understand how. The error in the openhab.log is:

2018-01-13 20:42:24.111 [WARN ] [rthome.model.script.actions.BusEvent] - Cannot convert '' to a state type which item 'tmp_dht22' accepts: [DecimalType, UnDefType].
2018-01-13 22:42:09.475 [INFO ] [el.core.internal.ModelRepositoryImpl] - Refreshing model 'aquabeet.rules'
2018-01-13 22:42:09.513 [WARN ] [el.core.internal.ModelRepositoryImpl] - Configuration model 'aquabeet.rules' is either empty or cannot be parsed correctly!
2018-01-13 22:42:09.774 [WARN ] [el.core.internal.ModelRepositoryImpl] - Configuration model 'aquabeet.rules' has errors, therefore ignoring it: [18,43]: no viable alternative at input '60'

2018-01-13 22:42:15.087 [ERROR] [ntime.internal.engine.ExecuteRuleJob] - Error during the execution of rule 'DHT22': An error occurred during the script execution: null
[22:42:23] openhabian@openHABianPi:~$ 

Just for completeness Questions 2 and 3:

  • Permissions are set correctly and the python script, run manually, works also fine and returns the right string. The DHT22 (as read on a tutorial) is kind of slow, taking ~5s between access for readings, and since I read temp and humidity separate, I put the sleeps in to give it time to respond and update the value.
  • The rule should start every 60 seconds at 3 seconds past the full minute, not every minute and three seconds. Or am I doing that wrong too? The 400ms pauses were a random fix-the-problem try (internal timing problems? - I thought). I will take them out again but they shouldn’t cause harm, since the script is over once the new minute starts anyways
  • REST / MQTT: I don’t know how to do that, I only had found a tutorial with executeCommand
  • same

In Conclusion, you may be pointing at more elegant solutions which I might look into later, but I would also like to understand the current way first - and the problem it has.
(The first line doesn’t worry me - the sensor probably didn’t respond in time, that happens and is ok for me). But what’s wrong here:

Configuration model 'aquabeet.rules' has errors, therefore ignoring it: [18,43]: no viable alternative at input '60'

Ok. Fair enough, one step at a time, ideal mindset for learning! :wink:

If you’re uploading the files via a sftp, openHAB at first tries to read the empty hull and then again, when the file reached EOF. hence two entries and the first one claiming an empty file.
The real error in the rule syntax is in line 18, column 43. Can you clarify the line as we don’t have the rest of the rules file?

Then again, the first line in the log tells you, the item ‘tmp_dht22’ received an update (empty string it seems), which doesn’t correspond with the item type (number). That means, your script didn’t come up with a decent value as you already pointed out (are you sure, the script returns only the expected value?).

Then again the rule’s trigger:
https://docs.openhab.org/configuration/rules-dsl.html

A cron expression takes the form of six or optionally seven fields:
  1. Seconds
  2. Minutes
  3. Hours
  4. Day-of-Month
  5. Month
  6. Day-of-Week
  7. Year (optional field)

So yes, to be clear every 60secs, but 3 secs into the full minute. I was concerned by the 3 secs, as crontab starts with minutes not seconds (some newbies are confused by that at first). and starting 3secs into a minute is a bit strange! :grin:

tmp_dht22.postUpdate(TEMP as DecimalType)

Converting the string to an DecimalType.

Is this to prevent updating when the Skript does not return a valid value? Maybe write the Skript to return ERROR or when something went wrong. So never returning an empty string.

Put this into a separate rule.

rule "DHT22"
when
        Item humidity_dht22 changed
then
       if(humidity_dht22.state < 60) { AquapumpSwitch.sendCommand("ON") } 
       else if(humidity_dht22.state > 65) { AquapumpSwitch.sendCommand("OFF") }
end 

No garanty for typos, written on my phone.

Leaving the empty string aside for now (thanks for the hint though, i will fix it later),

the Error location in the rule file, as the error also describes, is exactly at the 60. Same happens when I put it into its own rule, as Josar suggests. As if 60 wasn’t a valid comparison value.

It must have to do with the language, I know some Python and bash, but no Xtend or Java; But based on the docs, < is fine, and since I cast as DecimalType it should be comparable…

Ok, thinking a bit more (that the Problem must be in the comparison, I deleted

as DecimalType

and now it works. Typical case of having unsystematically added something to solve an error which in the end remained part of the problem after the original mistake was already solved. It ‘looks like’ it should cause no harm, but alas, without this it works (the state is already a number after all.

@biderth,
the REST API looks interesting, but then I have to outsource into crontab, for now I think I will keep it in the rule. What did you mean by this here though:

something like the following, instead of using Number variable HUMID (after error checking within the python script)?

humidity_dht22.postUpdate(executeCommandLine("/home/openhabian/dht22/read_temperature.py", 10000))

Thanks for hinting me to the logs and other solutions. If anyone can explain why casting as DecimalType doesn’t work it would be great, but I guess this is solved

Not sure actually, but maybe it has to do with this:

That indeed helps to understand, thanks!

strange indeed. I’ll tell you, what I do:

**items**
Number RTR_EG_WoZi_actualTemp	"Ist Wohnzimmer  [%.1f °C]"
Number Sensoren_Temp_OutS	"Temperatur Süd [%.1f °C]" 

**rule**
if (Sensoren_Temp_OutS.state >= 40 && RTR_EG_WoZi_actualTemp.state >= 21) {

this works for me… So I don’t see a difference here? (except you defined %.1f %% for the stateformat in a UI - which I don’t think is relevant here?)

edit: ok, now I read the next post in line:

so - glad to see that one fixed!

sure: basically you have two ways of update states of an item:

  1. from outside OH2 (as described REST-API or MQTT are the most common ones here)
  2. from inside OH2 (openHAB grabs the value - via bindings, rules, item-definitions)

let’s keep that in mind and go a little further. In an ideal world, you have a direct interface to every bit of Information and you can use that one directly. That means, if possible, try to use bindings for OH2, if possible. So, if your DHT22 is read out via onewire - have a look at the onewire binding. In nearly all cases, the development of a binding gives you the best possible Integration into your openHAB Installation and keeps you from having to maintain everything for yourself (you basically just configure the binding and get the items for free).
But you’re here to learn something different, so let’s assume, there is no binding for your DHT22 device to directly integrate its Information. So you have already written some python code, which does what’s it does and gets you an Output (here the humidity, if I understand correctly). Now, the basic principle in my eyes would be to have as little distributed calculations as possible. So either you’re doing all your stuff in openHAB and you just read raw data from your script - or (what I think works best) you do error-handling, formatting and stuff in your script and just present the tested output to openHAB.

So, what I’m proposing is

  • read out your DHT22
  • do all your error-handling (timeouts, faulty readings, …) in your python script
  • return only the result to openHAB

And here we are - you can now easily either have a rule within openHAB (as you already posted) to execute the script or you can execute the script externally and push its result to openHAB.
As said before - I’d prefer the latter, why? Because if there’s an ERROR you can just keep that from openHAB, so openHAB only gets clean result. Otherwise you have also two places for “real” errorhandling - you can of course have some kind of low level error handling, if something gets sideways in openHAB.

so, tl;dr:

  1. make your python script fool proof either way
  2. call it from either a rule or you can also attach it to the item directly
  3. or let the script update the item via REST or MQTT

Tipp:
If using the exec-binding directly, you can just put the output of the script as value (with Transformation if needed) to openHAB:

Thing exec:command:apc [command="/usr/local/bin/apcaccess  status", interval=15, timeout=5, autorun=false]

as exec is a 2.0 binding, it uses Things. If you need an item afterwards for better handling in a rule, just add it later on.

I don’t have a lot to add to the excellent advise already provided but there are a couple of side issues not yet addressed.

Long sleeps in rules are discouraged. It can cause problems in the long run. You stated that the sleep is to deal with the script sometimes taking a long time to run, but that is what the number you pass to execute command line is for.

For an example to learn from or perhaps an external script you can use see

We even have DHT22 support now.

1 Like