Problem using humidity in a rule

Using openhab3 I want to use humidity

var Number humitat_bany = tempBany_temp3humidity.state as DecimalType

But I can’t because it has a %:

2022-10-05 20:39:17.716 [ERROR] [internal.handler.ScriptActionHandler] - Script execution of rule with UID 'humitatBany-2' failed: Could not cast 62.7 % to org.openhab.core.library.types.DecimalType; line 63, column 31, length 43 in humitatBany

I configured the item as Number:Dimensionless; so I don’t know where does the ‘%’ come from, or how to use it in a rule besides it.

Anybody can help me?

By the way, the rule was used in openhab2 to start the “airflow” when humidity was hight (after a shower), and stop when it was ok.

This looks like Rules DSL.

It’s because what ever is populating that Item is providing units. In this case %.

As for how to use it in a rule, it depends on how you want to use it. It’s usually best to keep the units and do math and comparisons using values that have compatible units. In Rules DSL that’s as simple as using | %. For example,

if(tempBany_temp3humidity.state < 50 | %)

tempBany_temp3humidity.state + 5 | %

and so on.

Thanks a lot; it solved the problem.
I think I may have to rethink the whole rule, it don’t follow the KISS principle:

//keep automatic timer, so I can check if it is enabled and/or destoy it
var Timer timer_extractor = null
//keep manual timer. Not used right now, when enabling air it would stop automatically after 15 minutes
var Timer timer_extractor_manual = null
// average humidity, waiting for other sensors to be added
var DecimalType mitja_humitat
// for debuging only
val telegramAction = getActions("telegram","telegram:telegramBot:tenboig")

// too late to run; it stops the air if it is too late
rule "extractor_massa_tard"
when
   Time cron "0 5 23 * * ? *"
then
    if (timer_extractor !== null) {
        timer_extractor.cancel()
        timer_extractor = null
    }
    if (timer_extractor_manual !== null) {
        timer_extractor_manual.cancel()
        timer_extractor_manual = null
    }
    sendCommand(extractor1, OFF)
end

// shower on
rule "banyexceshumitat"
when
   Item tempBany_temp3humidity changed
then

    // used to calculate average humidity
    // var Number total = 0
    // var Number elements = 0
    // if (HumitatTermomenjador.state !== NULL) {
    //     elements = elements +1
    //     total = total + HumitatTermomenjador.state as DecimalType
    // }
    // if (HumitatDormitori.state !== NULL) {
    //     elements = elements + 1
    //     total = total + HumitatDormitori.state as DecimalType
    // }
    // if (Humitattemp2.state !== NULL) {
    //     elements = elements + 1
    //     total = total + Humitattemp2.state as DecimalType
    // }
    

    // if (elements > 0) {
    //     mitja_humitat = total/elements
    // }
    // else {
        mitja_humitat = new DecimalType(60)
    // }

    // var int minuts_extra = (2*(humitat_bany-mitja_humitat)).intValue+10
    var int minuts_extra = 15
    var Number hour = now.getHourOfDay()

    if (
              (extractor1.state === ON) 
        &&
                ((hour < 23)  && (hour >= 7))
        &&
        (
            tempBany_temp3humidity.state>(mitja_humitat + 15 | %)
            &&
            tempBany_temp3humidity.state >= 80|%
        )
    ){
        if (timer_extractor===null) {
            if (timer_extractor_manual !== null) {
                timer_extractor_manual.cancel()
                timer_extractor_manual = null
            }
            timer_extractor = createTimer(
                now.plusMinutes(minuts_extra),
                [|
                sendCommand(extractor1, OFF)
            ])
            sendCommand(extractor1, ON)
        }
        else {
            timer_extractor.reschedule(now.plusMinutes(minuts_extra));
        }
    }
end

// manual ventilation
rule "banyventilaciomanual"
when
    Item extractor1 changed to ON
then
   if ((timer_extractor===null) && (timer_extractor_manual===null)) { //el creem
        timer_extractor_manual=createTimer(
            now.plusMinutes(10),
                [|
                    sendCommand(extractor1, OFF)
                    timer_extractor_manual = null
                ])
   }
end

//remove timers if ventilation is stopped manually
rule "banyaturemventilacio"
when
    Item extractor1 changed to OFF
then
   if ((timer_extractor!==null)) {
       timer_extractor.cancel()
       timer_extractor=null
   }
   if (timer_extractor_manual!==null) {
       timer_extractor_manual.cancel()
       timer_extractor_manual=null
   }
end


Indeed, this could be made much more simply, especially if we can use the same number of minutes for the initial time (10 minutes) and the reschedule you could do the following:

  1. Set a 10 minute Expire metadata on extractor1 to turn it OFF after 10 minutes. That will eliminate timers, the global variables as well as the banyventilaciomanual and banyaturemventilacio rules entirely.

  2. The “banyexeshumitat” rule would become the following:

rule "banyexceshumitat"
when
   Item tempBany_temp3humidity changed
then
    // nothing to do if it's not already on
    if(extractor1.state != ON) {
        return;
    }

    val hour = now.getHourOfDay();
    val currHum = tempBany_temp3humidity.state

    if((hour < 23 && hour >= 7)  && currHum > 75 | %) { // The original test for >= 80% was redundant
        extractor1.sendCommand(ON) // resets the Expire timer
    }
end
  1. The “extractor_massa_tard” rule becomes:
rule "extractor_massa_tard"
when
   Time cron "0 5 23 * * ? *"
then
    extractor1.sendCommand(OFF) // will cancel the Expire
end

There is no reason to command extractor1 to ON again, you already tested and it’s already ON.

It’s pointless to test if the current humidity is > 75 (i.e. 60 + 15) and then test for >= 80.

Notice calling extractor1.sendCommand(ON) instead of sendCommand(extractor1, ON) (this is covered in the docs pretty extensively on the rules page for why.

Do not over specify the types of variables. Rules DSL works much better when you don’t.

Use val for any variable that should never change after you set it.

I can think of no reason you would ever want to create an use a DecimalType in a rule. Just let the language treat a number as a number, or even better, since it’s a percent, define it as such mitja_humitat = 60 |%

It’s pointless to test if the current humidity is > 75 (i.e. 60 + 15) and then test for >= 80.

The 60 should be the average of humidity, it could be 40 on dry days (when 60 would be high), or 70 on rainy ones (when 75 would be low); I have to redo this part.

Set a 10 minute Expire metadata

It wasn’t always 10 minutes… but this need some context:
The easiest way would be start the fan until sensors report low humidity; but my sensors sometimes missed reporting, so fan didn’t stopped. My solution was to set a timer, where time depended on the difference between average humidity and bath humidity. This way timer was set for ~40 minutes for 90%, or ~10 minutes for 60% and always stopped.

Right now my zigbee network seems more stable (I bought a better controller); so it may be time to reduce complexity.

About variables… I am used to PHP and JS, but don’t know much about DSL; I have a lot to learn.

Thanks a lot for your time (and fast answer); I will improve my code with your help.

Right but the if expression says “if the humidity is greater than 60 and it’s greater than 80”. That’s the part that doesn’t make sense.

In my above solution the fan will remain running until the humidity drops below 75 plus up to ten minutes. You could cut that last ten minutes off by sending the OFF command when the humidity drops too low, or continue to leave it to the Expire timer to turn it OFF. You don’t need to guess or calculate how long to keep it ON. Just keep it on until the conditions warrant turning it OFF.

rule "banyexceshumitat"
when
   Item tempBany_temp3humidity changed
then
    // nothing to do if it's not already on
    if(extractor1.state != ON) {
        return;
    }

    val hour = now.getHourOfDay();
    val currHum = tempBany_temp3humidity.state

    if((hour < 23 && hour >= 7)  && currHum > 75 | %) { // The original test for >= 80% was redundant
        extractor1.sendCommand(ON) // resets the Expire timer
    }
   else {
       extractor1.sendCommand(OFF)
    }
end