[SOLVED] How to use timer state as trigger in rule

I have read several threads without finding an accurate solution, thus creating a new thread. I have a sun screen (zip screen) which I would like to automatically activate/deactivate during certain months of year and time of day based on measured lux values. However, as the lux value can drop for a short instance (sun behind sky), I would like to include a timer as a condition (to avoid screen running up and down unnecessarily): I.e. if measured lux value is below a threshold for more than 5 minutes, THEN deactivate sun screen.

So, I have been playing around with a Proxy item to be used as a variable to dictate if sun screen should be active or not. Although, not sure how to use timer state (in a loop) as a condition to change Proxy state…?

My preliminary code:

var Timer MyTimer = null

rule "Solskjerming Proxy"
when
    Time cron "0 0 10-16 ? APR-OCT MON-SUN *"
then
    if (Vaerstasjon_WeatherIllumination.state > 45000) {
        ScreenProxyStue.sendCommand(ON)
        MyTimer = null
        logInfo("Sol", "Solskjerming ON")
    }
    else {
        MyTimer = createTimer(now.plusSeconds(1)) [ |
        if ((SolskjermingStue_Percentage.state as Number) = 100) {
            MyTimer.reschedule(now.plusMinutes(1))
            if (MyTimer >= 5) {
                ScreenProxyStue.sendCommand(OFF)
                logInfo("Sol", "Solskjerming OFF")
            }
        }
        else {
            MyTimer.cancel
            MyTimer = null
        }
        ]
    }
end

rule "Solskjerming ON"
when
    Item ScreenProxyStue changed to ON
then
    SolskjermingStue_Percentage.sendCommand(100)
end

rule "Solskjerming OFF"
when
    Item ScreenProxyStue changed to OFF
then
    SolskjermingStue_Percentage.sendCommand(0)
end

You probably want a moving avererage rather than a timer, there is probably useful code here (I don’t have shades)

var int Lux_morgens_hoch = Garten_Auge_Lux.averageSince(now.minusMinutes(5))

If you do want to use the timer route, look at Generic Presence Detection and Design Pattern: Motion Sensor Timer for example.

Essentially you just set a Timer, in Rules or Expire binding when the value drops below the threshold. If the value risers above the threshold, cancel the timer. Only if the value remains below the threshold for five minutes (i.e the timer want cancelled) do you open the shades.

I dislike using cron in an event driven system.
I’d trigger rules off of illumination changes and then find out if it’s daytime and in season in the body of the rule…

Appreciate the proposal. Realise it is a much smoother approach to use a moving average compared to my timer route.

So, I have adjusted my code as shown below. However, using Visual Studio Code, I get an error stating:

“Ambiguous binary operation.
The operator declarations
operator_greaterThan(Type, Number) in NumberExtensions and
operator_greaterThan(Number, Number) in NumberExtensions
both match.”

Any clue why?

rule "Solskjerming Proxy"
when
    Time cron "0 0 10-16 ? APR-OCT MON-SUN *"
then
    if (Vaerstasjon_WeatherIllumination.averageSince(now.minusMinutes(5)) > 45000) {
        ScreenProxyStue.sendCommand(ON)
        logInfo("Sol", "Solskjerming ON")
    }
    else if (Vaerstasjon_WeatherIllumination.averageSince(now.minusMinutes(5)) < 45000) {
        ScreenProxyStue.sendCommand(OFF)
        logInfo("Sol", "Solskjerming OFF")
        }
end

rule "Solskjerming ON"
when
    Item ScreenProxyStue changed to ON
then
    SolskjermingStue_Percentage.sendCommand(100)
end

rule "Solskjerming OFF"
when
    Item ScreenProxyStue changed to OFF
then
    SolskjermingStue_Percentage.sendCommand(0)
end

Xtend does not know the type of … averageSince(...) so it does not know which comparison code to use. Typecast that or make it 2 steps like I did in my example, i.e. assign it to a variable (to have a type) in step 1.

Almost. Ambiguous function call means that it does know what type both arguments are, but there are two potential methods that match all the possible types of the operands.

In this case averageSince is returning a value that is of type Type and of type Number. There are two operator_greaterThan methods that work with those two types and it can’t figure out which one you actually meant to call.

It’s a subtle difference but in certain circumstances it is useful to understand. If the engine could figure out the type of averageSince(), than there would have been an error similar to “could not find method operator_freaterThan(null, Number)”.

What is odd in this case is I thought averageSince returned a primitive so I’ve no idea where the Type is coming from.

Yes, quite strange. It seems as my “Vaerstasjon_WeatherIllumination” variable (being the actual lux measurement) is causing some issues.

Trying the two step approach, defining the averageSince as a separate variable (see below), I get the error: “Type mismatch: cannot convert from DecimalType to int”. “Greater than” then is ok. Switching variable to DecimalType causes error to the “Greater than” again.

var int Lux = Vaerstasjon_WeatherIllumination.averageSince(now.minusMinutes(5))

I’d replace your figures with a constant (val Number lux_threshold = 45000) which is better programming style anyway, then try val int or val DecimalType
Not sure how Xtend handles (untyped) figures, this might contribute to the problem.
(Or even use an item then you can change the value at runtime through UI).

That error is really l easy. Surprisingly, averageSince is returning a DecimalType instead of just a Number. That’s probably where the Type is coming from in the original error. If you want an int, you need to call .intValue. Though I recommended casting it to Number instead. Use val Number as Markus recommends and it should fix both errors.

Yes, your corrections worked, i.e. defining threshold values as constants (val Number), and also defining averageSince as a val Number.

So now I have a rule based on time cron (being tested every 5 minutes within the set time range), whereas averageSince is being calculated and checked against threshold values.