oH4: Rule stopped working after upgrade - Reminder for battery charging level

Hi there,

my SD card was almost dying so i ordered a new one and started with oH4 from a oH3.4 backup.
Almost everything started working as before, except for one rule.
This rule will be triggered by the battery level of my Apple Watch and should send me a message once the battery is over 75%.
This worked before the upgrade but stopped working with oH4 and i can´t find the problem.

The rule:

val telegramAction = getActions("telegram","telegram:telegramBot:bot")
val long bot1 = 1234 // me
val long bot2 = 5678 // wife

val String ruleId = "watch_Charging"

rule "watchMichael Charging Notification"

when

    Item MichaelWatch_BatteryLevel changed

then

    var BatteryMO = MichaelWatch_BatteryLevel.state.toString.split("\\.")
    val LadungMO = BatteryMO.get(0)
    val Voll = (MichaelWatch_BatteryLevel.state >= 75)

    var watchMBcurrent = MichaelWatch_BatteryLevel.state.toString
    var watchMBlast = MichaelWatch_BatteryLevel.previousState(true).state.toString

    if(watchMBcurrent < watchMBlast)
    {
        logDebug(ruleId, "Laden nicht aktiv.")
        return;
    }

    if((watchMBcurrent > watchMBlast) && Voll)
    {
        logDebug(ruleId, "Laden abgeschlossen mit " + LadungMO + "%. Benachrichtigung versendet.")
        telegramAction.sendTelegram(bot1, "Deine Apple Watch ist zu " + LadungMO + "%% geladen.")
    }
    else
    {
        logDebug(ruleId, "Laden noch nicht abgeschlossen, Akku zu " + LadungMO + "% geladen.")
    }

end

The item

Number MichaelWatch_BatteryLevel "Akku [%d %%]" <battery> (gPhones, gPersist) {channel="icloud:device:openhab:MichaelWatch:batteryLevel"}

The error:

2023-08-04 16:22:19.796 [ERROR] [internal.handler.ScriptActionHandler] - Script execution of rule with UID 'watchCharging-1' failed: Cannot invoke "org.openhab.binding.telegram.internal.action.TelegramActions.sendTelegram(java.lang.Long, String, Object[])" because "actions" is null in watchCharging
  • Platform information:
    • Hardware: Raspberry Pi 4 Model B Rev 1.1 - 4GB
    • OS: Raspbian GNU/Linux 11 (bullseye)
    • Java Runtime Environment:
      • openjdk version “17.0.7” 2023-04-18
      • OpenJDK Runtime Environment (build 17.0.7+7-Raspbian-1deb11u1rpt1)
      • OpenJDK Client VM (build 17.0.7+7-Raspbian-1deb11u1rpt1, mixed mode, emulated-client)
    • openHAB version: openHAB 4.0.1 - Release Build

kind regards
Michael

Is this a rules file or defined via UI ?
I think the UI does not support global variables ( variables outside the rule ).
Does it work when you move it into the rule definition ?

It‘s a .rules file.

Well, it’s no good idea to define the val telegramAction outside the rule, this should be a local value.

I guess there are two of these rules? Maybe consider to use some openHAB magic :slight_smile:

val long bot1 = 1234 // me
val long bot2 = 5678 // wife

rule "watchMichael Charging Notification"
when
    Item MichaelWatch_BatteryLevel changed or
    Item WifeWatch_BatteryLevel changed
then
    val telegramAction = getActions("telegram","telegram:telegramBot:bot")
    var nSOCnew = 0.0
    if(newState instanceof Number)
        nSOCnew = (newState as Number).floatValue
    val bCharged = (nSOCnew >= 75)
    val iSOCnew = nSOCnew.intValue

    var nSOCold = 0.0
    if(previousState instanceof Number)
        nSOCold = (previousState as Number).floatValue

    var theBot = bot1
    val strName = triggeringItemName.split("_").get(0)
    if(strName.contains("Wife"))
        theBot = bot2

    if(nSOCnew < nSOCold) {
        logDebug("watch_Charging", "Laden {} nicht aktiv.",strName)
        return;
    }

    if((nSOCnew > nSOCold) && bCharged) {
        logDebug("watch_Charging", "Laden {} abgeschlossen mit {} %. Benachrichtigung versendet.",strName ,iSOCnew)
        telegramAction.sendTelegram(theBot, "Deine Apple Watch ist zu %d %% geladen.",iSOCnew)
    } else
        logDebug("watch_Charging", "Laden {} noch nicht abgeschlossen, Akku zu {} % geladen.",strName ,iSOCnew)
end

Should do the job for both watches :slight_smile: (of course you have to change the name of the second Item)

2 Likes

Awesome!
I´ll try to understand what you did to achieve the same result :slight_smile:
My main problem is that i´m no developer, therefore i´m trying to have rules that i understand and can “maintain”.

Are the following descriptions correct?
nS0Cnew is the current value of the item
iS0Cnew is the current value as an integer for the message
nS0Cold is the previous value of the item
bCharged is the check that the current level is over 75%
strName is the name of the triggereing item to check between the wifes and mine

Thank you Udo!
I´ll give it a try and wait for the first watch to exceed 75% :smiley:

Atleast the VS Code openhab extension doesn´t like
nSOCnew = (newState as Number).floatValue
and
nSOCold = (previousState as Number).floatValue
because it shows me
Type mismatch: cannot convert from float to int

openHAB also doesn´t like that rule and gives me the following error:

2023-08-04 19:57:47.404 [ERROR] [internal.handler.ScriptActionHandler] - Script execution of rule with UID 'watchCharging-1' failed: An error occurred during the script execution: Could not invoke method: org.eclipse.xtext.xbase.lib.IntegerExtensions.operator_greaterEqualsThan(int,int) on instance: null in watchCharging

Oh, sorry, of course when definig the vars it has to be a float value…
Change both definitions to

var nSOCnew = 0.0
var nSOCold = 0.0

I have changed the code above :slight_smile:

You are at most right about the local values.
The main thing is, there are implicit variables when a rule is triggered, these are (here)
newState contains the current state of the item which triggered the rule.
previousState contains the old state of the item which triggered the rule (by trigger changed).
trigeringItemName contains the name of the item which triggered the rule.

The items are of type Number, there is no reason to build a string and strip parts to get a string without decimals.
Please be aware that you can’t compare strings with greater than or less than, there is only equal or not equal openHAB won’t give an error, but also it won’t give the correct output.

This:

if(newState instanceof Number)

is a test if newState is of Type Number, so the next command will never fail:

nSOCnew = (newState as Number).floatValue

will ensure that the value is numeric without additional unit.

As I don’t have an Apple Watch, which values do you get from BatteryLevel? Maybe it’s an Integer
anyway (0 %, 1 % … 99 %, 100 % but not e.g. 77.5 %)
If it’s only integer, the rule will get even less complex.

2 Likes

Did an addition to my posting :slight_smile:

Still no luck.

Script execution of rule with UID 'watchCharging-1' failed: An error occurred during the script execution: Could not invoke method: java.lang.Double.intValue() on instance: 22.0 in watchCharging

The item is defined as Number and the value is always XY.0 because the decimal doesn´t change.
Edit: I´m wrong and the value ca have a lot of decimal places 21.99999988079071

That’s a strange behavior.

How do you get the state of Charge from the watch? (i.e. which channel)

My bad, you already told in the first posting…

I don´t understand this either…

The item gets the state from a channel within the iCloud binding.

I’m pretty sure it’s an Integer value (percentType)
BUT maybe the channel has changed to be an UoM channel, If that’s true, see

and therefor change the item to

Number:Dimensionless MichaelWatch_BatteryLevel "Akku [%d %%]" <battery> (gPhones, gPersist) {channel="icloud:device:openhab:MichaelWatch:batteryLevel", unit="%"}

The rule should be able to handle the whole stuff with Integer values:

val long bot1 = 1234 // me
val long bot2 = 5678 // wife

rule "watchMichael Charging Notification"
when
    Item MichaelWatch_BatteryLevel changed or
    Item WifeWatch_BatteryLevel changed
then
    val telegramAction = getActions("telegram","telegram:telegramBot:bot")
    var iSOCnew = 0
    if(newState instanceof Number)
        iSOCnew = (newState as Number).intValue
    val bCharged = (iSOCnew >= 75)

    var iSOCold = 0
    if(previousState instanceof Number)
        iSOCold = (previousState as Number).intValue

    var theBot = bot2
    val strName = triggeringItemName.split("_").get(0)
    if(strName.contains("Michael"))
        theBot = bot1

    if(iSOCnew < iSOCold) {
        logDebug("watch_Charging", "Laden {} nicht aktiv.",strName)
        return;
    }

    if((iSOCnew > iSOCold) && bCharged) {
        logDebug("watch_Charging", "Laden {} abgeschlossen mit {} %. Benachrichtigung versendet.",strName ,iSOCnew)
        telegramAction.sendTelegram(theBot, "Deine Apple Watch ist zu %d %% geladen.",iSOCnew)
    } else
        logDebug("watch_Charging", "Laden {} noch nicht abgeschlossen, Akku zu {} % geladen.",strName ,iSOCnew)
end

Please be aware that you can’t test the rule by triggering it manually, it has to be triggered by a Item changed event

Same result :frowning:

2023-08-05 14:32:57.315 [ERROR] [internal.handler.ScriptActionHandler] - Script execution of rule with UID 'watchCharging-1' failed: An error occurred during the script execution: Could not invoke method: java.lang.Double.intValue() on instance: 37.0 in watchCharging
Number:Dimensionless MichaelWatch_BatteryLevel "Akku [%d %%]" <battery> (gPhones, gPersist) {channel="icloud:device:openhab:MichaelWatch:batteryLevel", unit="%"}

So I did a short test here and can assure that the rule is just working as intended.

Is there any other content in the .rules file?

Only the Telegram IDs of the bots and a val for the ruleId.

That´s the complete file of watchCharging.rules

val long bot1 = 1234 // me
val long bot2 = 5678 // wife

val String ruleId = "WatchCharging"

rule "watchMichael Charging Notification"
when
    Item MichaelWatch_BatteryLevel changed or
    Item WifeWatch_BatteryLevel changed
then
    val telegramAction = getActions("telegram","telegram:telegramBot:bot")
    var nCurrentLevel = 0
    if(newState instanceof Number)
        nCurrentLevel = (newState as Number).floatValue
    val bCharged = (nCurrentLevel >= 75)
    val iCurrentLevel = nCurrentLevel.intValue

    var nLastLevel = 0
    if(previousState instanceof Number)
        nLastLevel = (previousState as Number).floatValue

    var theBot = bot1
    val strName = triggeringItemName.split("_").get(0)
    if(strName.contains("Wife"))
        theBot = bot2

    if(nCurrentLevel < nLastLevel) {
        logDebug(ruleId, "Laden {} nicht aktiv.",strName)
        return;
    }

    if((nCurrentLevel > nLastLevel) && bCharged) {
        logDebug(ruleId, "Laden {} abgeschlossen mit {} %. Benachrichtigung versendet.",strName ,iCurrentLevel)
        telegramAction.sendTelegram(theBot, "Deine Apple Watch ist zu %d %% geladen.",iCurrentLevel)
    } else
        logDebug(ruleId, "Laden {} noch nicht abgeschlossen, Akku zu {} % geladen.",strName ,iCurrentLevel)
end

Item

Number:Dimensionless MichaelWatch_BatteryLevel "Akku [%d %%]" <battery> (gPhones, gPersist) {channel="icloud:device:openhab:MichaelWatch:batteryLevel", unit="%"}

The channels for these items don´t have any configuration.

Did you already restart openHAB? (last hope…)

It’s very strange to get this “can’t invoke” message, as .intValue is definitely part of Number. The message makes no sense.

I did a restart and put a logInfo between every action in the rule.
It works until this part comes up where we compare the currentLevel against 75.
val bCharged = (nCurrentLevel >= 75)

And this if also will be triggered

    if(newState instanceof Number)
        nCurrentLevel = (newState as Number).floatValue

because i modified it and put another logInfo into the if.

    if(newState instanceof Number) {
        nCurrentLevel = (newState as Number).floatValue
logInfo(ruleId, "03.1. Get current state as Number. Value: " + nCurrentLevel)
    }

Output of the log:

2023-08-05 15:30:36.552 [INFO ] [nhab.core.model.script.WatchCharging] - 01. Start
2023-08-05 15:30:36.555 [INFO ] [nhab.core.model.script.WatchCharging] - 02. Load telegramAction. Value: org.openhab.binding.telegram.internal.action.TelegramActions@1234
2023-08-05 15:30:36.557 [INFO ] [nhab.core.model.script.WatchCharging] - 03. Create nCurrentLevel var. Value: 0
2023-08-05 15:30:36.560 [INFO ] [nhab.core.model.script.WatchCharging] - 03.1. Get current state as Number. Value: 65.0
2023-08-05 15:30:36.566 [ERROR] [internal.handler.ScriptActionHandler] - Script execution of rule with UID 'watchCharging-1' failed: An error occurred during the script execution: Could not invoke method: org.eclipse.xtext.xbase.lib.IntegerExtensions.operator_greaterEqualsThan(int,int) on instance: null in watchCharging

Ah. If you create a local variable without setting its type (which is totally fine and normally the best way to do it - let openHAB chose the type), you have to ensure that the type will be the correct one. here

var nCurrentLevel = 0 <-- will become Integer
nCurrentLevel = (newState as Number).floatValue <-- cannot invoke float to int

same for nLastLevel
so either type

var Number nCurrentLevel = 0 <-- will become Number

or

var nCurrentLevel = 0.0 <-- will also become Number

Ok one step further :slight_smile:

2023-08-05 16:04:19.573 [INFO ] [nhab.core.model.script.WatchCharging] - 01. Start
2023-08-05 16:04:19.578 [INFO ] [nhab.core.model.script.WatchCharging] - 02. Load telegramAction. Value: org.openhab.binding.telegram.internal.action.TelegramActions@1234
2023-08-05 16:04:19.580 [INFO ] [nhab.core.model.script.WatchCharging] - 03. Create nCurrentLevel var. Value: 0.0
2023-08-05 16:04:19.584 [INFO ] [nhab.core.model.script.WatchCharging] - 03.1. Get current state as Number. Value: 27.000002
2023-08-05 16:04:19.587 [INFO ] [nhab.core.model.script.WatchCharging] - 04. Check charging level. Value: false
2023-08-05 16:04:19.588 [ERROR] [internal.handler.ScriptActionHandler] - Script execution of rule with UID 'watchCharging-1' failed: An error occurred during the script execution: Could not invoke method: java.lang.Double.intValue() on instance: 27.000002 in watchCharging

05 would be val iCurrentLevel = nCurrentLevel.intValue
Could it be that .intValue can´t be used for nCurrentLevel because it gets loaded as (newState as Number).floatValue?

That’s the whole point.
This message doesn’t make sense at all, as .intValue is defined for Number and therefor also for Double and Float. (Number is parent to both)
Another question would be why it’s built as Double, makes also no sense.

I removed the .intValue from val iCurrentLevel = nCurrentLevel and now it´s working until if(nCurrentLevel < nLastLevel)

2023-08-05 16:24:22.102 [INFO ] [nhab.core.model.script.WatchCharging] - 09.1. Not charging

I´m currently charging to get a value over 75% and see what´s happening.

Is it worth raising a Github issue for this?


Edit:
Ok it´s not working as the telegramAction now fails because iCurrentLevel is a Number and can´t be used for the message…

nCurrentLevel = (newState as Number).floatValue
val iCurrentLevel = nCurrentLevel
telegramAction.sendTelegram(theBot, "Deine Apple Watch ist zu %d %% geladen.",iCurrentLevel)
2023-08-05 16:45:11.140 [INFO ] [nhab.core.model.script.WatchCharging] - 01. Start
2023-08-05 16:45:11.144 [INFO ] [nhab.core.model.script.WatchCharging] - 02. Load telegramAction. Value: org.openhab.binding.telegram.internal.action.TelegramActions@1234
2023-08-05 16:45:11.146 [INFO ] [nhab.core.model.script.WatchCharging] - 03. Create nCurrentLevel var. Value: 0.0
2023-08-05 16:45:11.149 [INFO ] [nhab.core.model.script.WatchCharging] - 03.1. Get current state as Number. Value: 90.93457
2023-08-05 16:45:11.153 [INFO ] [nhab.core.model.script.WatchCharging] - 04. Check charging level. Value: true
2023-08-05 16:45:11.155 [INFO ] [nhab.core.model.script.WatchCharging] - 05. Load current state as integer. Value: 90.93457
2023-08-05 16:45:11.157 [INFO ] [nhab.core.model.script.WatchCharging] - 06. Create nLastLevel var. Value: 0.0
2023-08-05 16:45:11.161 [INFO ] [nhab.core.model.script.WatchCharging] - 06.1. Get last state as Number. Value: 86.0
2023-08-05 16:45:11.163 [INFO ] [nhab.core.model.script.WatchCharging] - 07. Get bot1 ID. Value: 134757258
2023-08-05 16:45:11.166 [INFO ] [nhab.core.model.script.WatchCharging] - 08. Check who triggered the rule. Value: MichaelWatch
2023-08-05 16:45:11.167 [INFO ] [nhab.core.model.script.WatchCharging] - 09. Start comparsion
2023-08-05 16:45:11.168 [INFO ] [nhab.core.model.script.WatchCharging] - 10. Start comparsion if charging
2023-08-05 16:45:11.170 [INFO ] [nhab.core.model.script.WatchCharging] - 10.1. Charging active!
2023-08-05 16:45:11.172 [ERROR] [internal.handler.ScriptActionHandler] - Script execution of rule with UID 'watchCharging-1' failed: d != java.lang.String in watchCharging

Edit2:
It´s almost working!

telegramAction.sendTelegram(theBot, "Deine Apple Watch ist zu % geladen.",iCurrentLevel)

Deine Apple Watch ist zu 95,9446eladen.

Only 2 things to clear :slight_smile:

  1. Get the correct message with the level and %
  2. Strip the decimal places…

Edit3:
I know it´s not the best way but i don´t care as long as it works :stuck_out_tongue:

telegramAction.sendTelegram(theBot, "Deine Apple Watch ist zu " + iCurrentLevel + "%% geladen.")

Deine Apple Watch ist zu 89.11115% geladen.

Now i want to remove the decimal places like .11115 from that value.
Normally i would try to make this a String but i´m sure this wont work here.

Any ideas or approaches?

Thank you Udo!!!