Error during subtraction of INT values

  • Platform information:
    • Hardware: Raspberry Pi 4 Model B Rev 1.1
    • OS: Raspbian GNU/Linux 10 (buster)
    • Java Runtime Environment: openjdk version “1.8.0_252”
    • openHAB version: openHAB 2.5.7-1 (Release Build)
  • Issue of the topic: Error during substraction of INT values

Dear community,

I recently got more involved with OpenHab thereby setting up a view things I found in this forum. I also want to include the “Wake Up Alert” as described by Seaside.

The original code was running fine, but I wanted to make a view adaptions such that I have the option of preset times between which I can easily switch. Therefore i created the following items:

//Items for WakeUp Sunrise Light
Group                       SunriseWakeUpAlarm
Group:Switch:OR(ON,OFF)     gSunriseAlert       "Switches for Sunrise Alert"    <switch>
Number SunriseWakeUpHour                        "Wake up hour [%d]"             <time>      (SunriseWakeUpAlarm)
Number SunriseWakeUpMinute                      "Wake up minute [%d]"           <time>      (SunriseWakeUpAlarm)
Number SunriseWakeUpDimFadeInTime               "Dimmer Fade in Time [%d]"      <time>      (SunriseWakeUpAlarm)
Switch SunriseWakeUpAlarmCustomSwitch           "Wake up alarm [%s]"                        (SunriseWakeUpAlarm, gSunriseAlert, gRuleSwitches)
String SunriseWakeUpAlarmDefaultMapping                                                     (SunriseWakeUpAlarm, gRuleSwitches)
Switch SunriseWakeUpAlarmHelperSwitch           "Dummy for Default Switch  [%s]"            (SunriseWakeUpAlarm, gSunriseAlert, gRuleSwitches)
Switch SunriseWakeUpAlarmTriggered              "Wake up alarm triggered [%s]"  <alarm>     (SunriseWakeUpAlarm)

I use the item "SunriseWakeUpAlarmDefaultMapping " in the sitemap to quickly choose between 4 presets. The sitemap looks as follows:
sitemap rules label="Rules" {

    Switch item=gRuleSwitches mappings=[OFF="All Off", ON="All On"]
    Frame label="Rules to turn ON/OFF" {
        Switch item=Window_Open_Switch
    }  

    Text item=gSunriseAlert label="Wake up Sunrise [%s]" icon="time" {
        Setpoint item=SunriseWakeUpDimFadeInTime minValue=10 maxValue=100 step=5
        Switch label="" item=SunriseWakeUpAlarmDefaultMapping mappings=["OFF"="OFF", "0700"="7:00", "0730"="7:30", "0800"="8:00", "0830"="8:30"]
        Frame label="Custome Time" {
            Setpoint item=SunriseWakeUpHour minValue=0 maxValue=23 step=1
            Setpoint item=SunriseWakeUpMinute minValue=0 maxValue=59 step=1
            Switch item=SunriseWakeUpAlarmCustomSwitch
        }
        Text item=SunriseWakeUpAlarmTriggered
    }
}

As soon as one of the “presets” is chosen, the item “SunriseWakeUpAlarmHelperSwitch” is set to “ON” in order for the group for the actual rule to be triggered. Further i adapted the orginal script by introducing the possibility to choose the “fade in time” for the bulbs instead of the “dim steps” and “time step”. This value is stored in the item “SunriseWakeUpDimFadeInTime”. The problem occurs when I want to substract the SunriseWakeUpDimFadeInTime from the “totalMinTillDimStart”, which is the time between now (when i set the timer) and the time when the alert is set. Following the rule. Below it I´ll attach the log output
import org.eclipse.smarthome.core.library.types.DecimalType
import org.eclipse.smarthome.model.script.actions.Timer

var boolean mAlaramTriggered = false 
var int mCurrentDimLevel = 0
val String LOG = "SunriseWakeUp"
var Timer mWakeUpTimer = null
var Timer mDimmerTimer = null
var int mTimeStep
val int DEFAULT_DIM_FADE_IN = 30
val int DEFAULT_TIME_STEP = 1
//var int SunriseWakeUpHour = 0
//val int SunriseWakeUpMinute = 0

rule "SetHelperSwitchOnOff"
when
    Item SunriseWakeUpAlarmDefaultMapping changed
then
    if ((SunriseWakeUpAlarmDefaultMapping.state.toString.contains("0700")) || (SunriseWakeUpAlarmDefaultMapping.state.toString.contains("0730")) || (SunriseWakeUpAlarmDefaultMapping.state.toString.contains("0800")) || (SunriseWakeUpAlarmDefaultMapping.state.toString.contains("0830"))) { 
        SunriseWakeUpAlarmCustomSwitch.sendCommand(OFF)
        Thread::sleep(100)
        SunriseWakeUpAlarmHelperSwitch.sendCommand(ON)
        logInfo(LOG, "Setting dummy Switch ON")
        logInfo(LOG, SunriseWakeUpAlarmDefaultMapping.state.toString())
    }
    else {
        logInfo(LOG, "Setting dummy Switch OFF")
        SunriseWakeUpAlarmHelperSwitch.sendCommand(OFF)
    }
end

rule "SunriseWakeUpAlarmSwitchOn"
when
    Item gSunriseAlert changed to ON
then
    var int SunriseWakeUpHour = SunriseWakeUpHour.state as Number
    var int SunriseWakeUpMinute = SunriseWakeUpMinute.state as Number

    // Set time from custom timer
    if (SunriseWakeUpAlarmCustomSwitch == ON) {
        SunriseWakeUpHour = SunriseWakeUpHour
        SunriseWakeUpMinute =  SunriseWakeUpMinute
        //SunriseWakeUpHour = (SunriseWakeUpHour.state as DecimalType).intValue
        //SunriseWakeUpMinute =  (SunriseWakeUpMinute.state as DecimalType).intValue
        logInfo(LOG, "Custome Sunrise set to: " + SunriseWakeUpHour + ":" + SunriseWakeUpMinute)
    }
    // Set time from default timers
    switch(SunriseWakeUpAlarmDefaultMapping.state) {
        case "0700": {
            SunriseWakeUpHour = 7
            SunriseWakeUpMinute = 0
            logInfo(LOG, "SunriseAlert set to: " + SunriseWakeUpHour + ":" + SunriseWakeUpMinute)
        }
        case "0730": {
            SunriseWakeUpHour = 7
            SunriseWakeUpMinute = 30
            logInfo(LOG, "SunriseAlert set to: " + SunriseWakeUpHour + ":" + SunriseWakeUpMinute)
        }
        case "0800": {
            SunriseWakeUpHour = 8
            SunriseWakeUpMinute = 0
            logInfo(LOG, "SunriseAlert set to: " + SunriseWakeUpHour + ":" + SunriseWakeUpMinute)
        }
        case "0830": {
            SunriseWakeUpHour = 8
            SunriseWakeUpMinute = 30
            logInfo(LOG, "SunriseAlert set to: " + SunriseWakeUpHour + ":" + SunriseWakeUpMinute)
        }
    }
    logInfo(LOG, "After Def. and Switch Hour is = " + SunriseWakeUpHour + " & Minute is = " + SunriseWakeUpMinute)
    //get current Hour/Minute
    var int nowHour = now.getHourOfDay
    logInfo(LOG, "NowHour set to: " + nowHour)
    var int hours = 0

    //logInfo(LOG, "hours set to: " + hours)
    val int hourDiff = SunriseWakeUpHour - nowHour
    logInfo(LOG, "hourDiff set to: " + hourDiff)
    if (hourDiff >= 0) {
        hours = hourDiff
    } else {
        hours = 24 + SunriseWakeUpHour - nowHour
    }

    logInfo(LOG, "hours set to: " + hours)
    var int minutes = 0
    val int nowMinute = now.getMinuteOfHour
    var int minuteDiff = SunriseWakeUpMinute - nowMinute

    if (minuteDiff >= 0) {
        minutes = minuteDiff
    } else {
        minutes = 60 + SunriseWakeUpMinute - nowMinute
        if (hourDiff == 0) {
            hours = 24
        }
        hours--
    }
    var int SunriseWakeUpDimFadeInTime = SunriseWakeUpDimFadeInTime.state as Number
    /*
    if (SunriseWakeUpDimFadeInTime == NULL) {
        logInfo(LOG, "Fade in time not defined using default: 30min")
        SunriseWakeUpDimFadeInTime = DEFAULT_DIM_FADE_IN 
    } else {
        SunriseWakeUpDimFadeInTime = SunriseWakeUpDimFadeInTime
        logInfo(LOG, "Fade in time set to: " + SunriseWakeUpDimFadeInTime + " min")
    }
    */
    logInfo(LOG, "hours: " + hours + " //// minutes: " + minutes + " //// Sunrise: " + SunriseWakeUpDimFadeInTime)
    val int totalMinTillDimStart = (hours * 60) + minutes
    logInfo(LOG, "tmp totalMinTillDimStart: " + totalMinTillDimStart)
    // Substract 30 in order to start the dimming 30min prior to desired wakeup time
    totalMinTillDimStart =  totalMinTillDimStart - SunriseWakeUpDimFadeInTime
    logInfo(LOG, "totalMinTillDimStart: " + totalMinTillDimStart)
    var int hours_dimming = hours
    val int mins_dimming = (totalMinTillDimStart - (hours_dimming.intValue()*60))
    if (mins_dimming <= 0) {
        hours_dimming = hours_dimming - 1
        mins_dimming = mins_dimming + 60
    }
    logInfo(LOG, "Lights will start dimming up in: " + totalMinTillDimStart + " minutes i.e. " + hours_dimming + " h " + mins_dimming + " min")
    logInfo(LOG, "Time until you´ll have to wakeup: "  + hours + " h " + minutes + " min")
    SunriseWakeUpAlarmTriggered.sendCommand(OFF)
    if (mWakeUpTimer != null) {
        mWakeUpTimer.cancel
        mWakeUpTimer = null
    }
    mWakeUpTimer = createTimer(now.plusMinutes(totalMinTillDimStart), [|
                                        logInfo(LOG, "Alarm triggered! ")
                                        SunriseWakeUpAlarmTriggered.sendCommand(ON)
                                        mWakeUpTimer = null
                                        ])    
end

rule "SunriseWakeUpAlarmTriggeredOn"
when
    Item SunriseWakeUpAlarmTriggered changed to ON
then
    logInfo(LOG, "Alarm trigger starting to dim")
    mCurrentDimLevel = 0
    var int SunriseWakeUpDimFadeInTime = SunriseWakeUpDimFadeInTime.state as Number
    /*
    if (SunriseWakeUpDimFadeInTime.state == NULL) {
        logInfo(LOG, "Fade in time not defined using default: 30min")
        SunriseWakeUpDimFadeInTime = DEFAULT_DIM_FADE_IN 
    } else {
        SunriseWakeUpDimFadeInTime =  (SunriseWakeUpDimFadeInTime.state as DecimalType).intValue
    }
    */
    var int mDimStep = 100 / SunriseWakeUpDimFadeInTime
    logInfo(LOG, "mDimStep: " + mDimStep)
    mDimmerTimer = createTimer(now.plusMinutes(1), [|
                                mCurrentDimLevel += mDimStep
                                logInfo(LOG, "Dimming " + mCurrentDimLevel)
                                if (mCurrentDimLevel >= 100) {
                                    logInfo(LOG,"Dimming is at its max, finished")
                                    mCurrentDimLevel = 100
                                } else {
                                    mDimmerTimer.reschedule(now.plusMinutes(1))
                                } 
                                gDimTVLights.sendCommand(mCurrentDimLevel)
                                if (mCurrentDimLevel == 100) {
                                    SunriseWakeUpAlarmSwitch.sendCommand(OFF)
                                }
                    ])

end
rule "SunriseWakeUpAlarmSwitchOff"
when
    Item gSunriseAlert changed to OFF
then
   logInfo(LOG, "Switch off wakeup")
   if (mDimmerTimer != null) {
       mDimmerTimer.cancel
       mDimmerTimer = null
   }
   if (mWakeUpTimer != null) {
       mWakeUpTimer.cancel
       mWakeUpTimer = null
   }
   SunriseWakeUpAlarmTriggered.sendCommand(OFF)
end

Log Output:

2020-10-25 00:23:53.834 [INFO ] [smarthome.model.script.SunriseWakeUp] - Setting dummy Switch OFF
2020-10-25 00:23:53.847 [INFO ] [smarthome.model.script.SunriseWakeUp] - Switch off wakeup
2020-10-25 00:23:59.843 [INFO ] [smarthome.model.script.SunriseWakeUp] - Setting dummy Switch ON
2020-10-25 00:23:59.853 [INFO ] [smarthome.model.script.SunriseWakeUp] - 0730
2020-10-25 00:23:59.902 [INFO ] [smarthome.model.script.SunriseWakeUp] - SunriseAlert set to: 7:30
2020-10-25 00:23:59.910 [INFO ] [smarthome.model.script.SunriseWakeUp] - After Def. and Switch Hour is = 7 & Minute is = 30
2020-10-25 00:23:59.916 [INFO ] [smarthome.model.script.SunriseWakeUp] - NowHour set to: 0
2020-10-25 00:23:59.925 [INFO ] [smarthome.model.script.SunriseWakeUp] - hourDiff set to: 7
2020-10-25 00:23:59.930 [INFO ] [smarthome.model.script.SunriseWakeUp] - hours set to: 7
2020-10-25 00:23:59.943 [INFO ] [smarthome.model.script.SunriseWakeUp] - hours: 7 //// minutes: 7 //// Sunrise: 35
2020-10-25 00:23:59.950 [INFO ] [smarthome.model.script.SunriseWakeUp] - tmp totalMinTillDimStart: 427
2020-10-25 00:23:59.954 [ERROR] [ntime.internal.engine.RuleEngineImpl] - Rule ‘SunriseWakeUpAlarmSwitchOn’: An error occurred during the script execution: Could not invoke method: org.eclipse.xtext.xbase.lib.IntegerExtensions.operator_minus(int,int) on instance: null

As the log shows, the values for the variables are set to

  • hours: 7
  • minutes: 7
  • sunrise: 35 (i.e. the time set in SunriseWakeUpDimFadeInTime)

Can some one assist me with this one?

The cause of the error is clear: a value is either not set yet or there is a conversion error.

‘SunriseWakeUpAlarmSwitchOn’: An error occurred during the script execution: Could not invoke method: org.eclipse.xtext.xbase.lib.IntegerExtensions.operator_minus(int,int) on instance: nul

To be honest, you need to simplify your problem in order for people to help. You paste a lot of text there, not many people are willing to go through that.

So please isolate the problem, and pin point exactly the error line. Only keep what lead up to that line.

Thanks for the reply and guidance. I thought supplying all information would avoid missing something, but I totally get your point.
To pick up on your suggestion:
The error occurs in the following operation

logInfo(LOG, "hours: " + hours + " //// minutes: " + minutes + " //// Sunrise: " + SunriseWakeUpDimFadeInTime)
val int totalMinTillDimStart = (hours * 60) + minutes
logInfo(LOG, "tmp totalMinTillDimStart: " + totalMinTillDimStart)
// Substract 30 in order to start the dimming 30min prior to desired wakeup time
totalMinTillDimStart =  totalMinTillDimStart - SunriseWakeUpDimFadeInTime

I also assumed, that the value for “SunriseWakeUpDimFadeInTime” might not be set, hence I logged it right before it´s used, which shows that the value is set to “35” and should be substractable from “tmp totalMinTillDimStart”.

2020-10-25 00:23:59.943 [INFO ] [smarthome.model.script.SunriseWakeUp] - hours: 7 //// minutes: 7 //// Sunrise: 35
2020-10-25 00:23:59.950 [INFO ] [smarthome.model.script.SunriseWakeUp] - tmp totalMinTillDimStart: 427
2020-10-25 00:23:59.954 [ERROR] [ntime.internal.engine.RuleEngineImpl] - Rule ‘SunriseWakeUpAlarmSwitchOn’: An error occurred during the script execution: Could not invoke method: org.eclipse.xtext.xbase.lib.IntegerExtensions.operator_minus(int,int) on instance: null

Hence the problem can´t be caused by the variable not being set. How can i check the datatype of variables? I´d assume both of the involved variables to be of type integer

You’re trying to set a variable totalMinTillDimStart by performing maths with this same variable (totalMinTillDimStart - SunriseWakeUpDimFadeInTime) - does openHAB handle this well? Have you tried using a differently named variable to store the result?

EDIT:
Hmmm, no, that’s not it. I tried the below rule, and it outputs without error:

rule "Test rule1"
when
    Item sTestSwitch changed
then
    val hours = 7
    val minutes = 7
    val SunriseWakeUpDimFadeInTime = 35
    val totalMinTillDimStart = (hours * 60) + minutes
    logInfo("Test","1. totalMinTillDimStart: " + totalMinTillDimStart)
    totalMinTillDimStart =  totalMinTillDimStart - SunriseWakeUpDimFadeInTime
    logInfo("Test","2. totalMinTillDimStart: " + totalMinTillDimStart)
end

Triggering this shows the following in the log:

15:48:55.496 [INFO ] [g.eclipse.smarthome.model.script.Test] - 1. totalMinTillDimStart: 427
15:48:55.513 [INFO ] [g.eclipse.smarthome.model.script.Test] - 2. totalMinTillDimStart: 392

However, when saving the rules file I do get a validation issue in the log:

15:49:03.449 [INFO ] [del.core.internal.ModelRepositoryImpl] - Validation issues found in configuration model 'test.rules', using it anyway:
Assignment to final variable

EDIT2:

Also worth noting that according to this, val cannot be re-assigned, whilst var can. Not sure why the above code worked, then…

As general comments -

DSL rules do not handle primitive variable types (like int) at all well. Doing sufficiently complicated maths with them can result in rules that are both slow to compile and slow to execute. Best avoided.

Number is essentially the “native” numeric format for DSL rules. It usually pays not to even bother trying to pre-define types, let the parser sort it out. Number types can hold integers too, 3.0 is distinct from 3 (but 3 is still a Number and not an int type)
var someVariable = 3

Of course sometimes we still need to get integer types when other functions need them. That’s okay, we can do that at the last possible moment e.g.
sometime = now.plusMinutes(someVariable.intValue)

Okay so I removed all the variable types when creating the variables and used “.intValue” for the timers and it works now.

Thanks :slight_smile:

1 Like