Heating rule with coming back previous Thermostat mode

Hello,
I try to write a rule for my radiator dependent on the window status sensor. I manage to switch thermostat off when the window is open but I wish to come back to the last Thermostat mode after minute when the window will be closed.
I am pretty sure it might te done in more efficient way with “received update” and switch case. Unfortunately I am not a programmer… Can someone help?

This is my try:

rule "Action on Window open in Office"
when
    Item BS_Office_Window changed to OPEN 
then
    if (BS_Office_Thermostat_Mode.state != 0) {
        BS_Office_Thermostat_Mode.sendCommand (0)
        logInfo("WINDOW", "Switching Thermostat OFF")
    }
    else   
        logInfo("WINDOW", "No action on Heating")
end

rule "Action on Window closed in Office"
when
    Item BS_Office_Window changed to Closed 
then
    ...wait a minute and switch to previous Thermostat mode...
end

Item BS_Office_Window is a FIBARO Window/Door sensor
Item BS_Office_Thermostat_Mode is my Thermostat and its status (0 - OFF, 1 - Heat, 15 -Full Heating etc.)

Save the state of BS_Office_Thermostat_Mode to a global variable. When the window closes, send command that global variable to restore to the state it had when the window was opened.

Hey Rich,
coming back after while with some improvements.
Here is the rule:

var Number TempVar
var Number CurrentThermSetTemp
var Number CurrentThermMode

rule "Action on Window open in Office"

when
    Item BS_Office_Window changed
    
then
    if ((BS_Office_Window.state == OPEN) && (BS_Office_Thermostat_Mode.state != 0)) {

        CurrentThermSetTemp = BS_Office_Thermostat_SetpointHeat.state
        CurrentThermMode = BS_Office_Thermostat_Mode.state
        TempVar = 1

        logInfo("WINDOW OFFICE", "Temp Var: " + TempVar)
        logInfo("WINDOW OFFICE", "Window: " + BS_Office_Window.state)
        logInfo("WINDOW OFFICE", "Therm Set Temp: " + CurrentThermSetTemp)
        logInfo("WINDOW OFFICE", "Therm Mode: " + CurrentThermMode)
        
        BS_Office_Thermostat_Mode.sendCommand (0)       //Set mode to OFF        
    }

    if ((BS_Office_Window.state == CLOSED) && (TempVar == 1)){  
        BS_Office_Thermostat_SetpointHeat.state = CurrentThermSetTemp
        BS_Office_Thermostat_Mode.state = CurrentThermMode
        TempVar = 0
        
        logInfo("WINDOW OFFICE", "Temp Var: " + TempVar)
        logInfo("WINDOW OFFICE", "Window:" + BS_Office_Window.state)
        logInfo("WINDOW OFFICE", "Therm Set Temp: " + BS_Office_Thermostat_SetpointHeat.state)
        logInfo("WINDOW OFFICE", "Therm Mode:" + BS_Office_Thermostat_Mode.state)
    }
    
end

and it works but…
I am afraid the rule is faster than my valve because I see in logs the mode has been changed to HEAT but the valve itsefl did not. “OFF” is displayed on it even if the items says HEAT and I do not hear rection that valve is opening again.

Could you/someone more experience judge the rule itself?
How can i introduce timing in the rule?
Thermostate shall switch off after period of time when the window is open.

LOG

2020-10-26 12:19:51.373 [vent.ItemStateChangedEvent] - BS_Office_Window changed from CLOSED to OPEN
==> /var/log/openhab2/openhab.log <==
2020-10-26 12:19:57.149 [INFO ] [smarthome.model.script.WINDOW OFFICE] - Temp Var: 1
2020-10-26 12:19:57.153 [INFO ] [smarthome.model.script.WINDOW OFFICE] - Window: OPEN
2020-10-26 12:19:57.156 [INFO ] [smarthome.model.script.WINDOW OFFICE] - Therm Set Temp: 23
2020-10-26 12:19:57.159 [INFO ] [smarthome.model.script.WINDOW OFFICE] - Therm Mode: 1
==> /var/log/openhab2/events.log <==
2020-10-26 12:19:57.165 [ome.event.ItemCommandEvent] - Item 'BS_Office_Thermostat_Mode' received command 0
2020-10-26 12:19:57.187 [nt.ItemStatePredictedEvent] - BS_Office_Thermostat_Mode predicted to become 0
2020-10-26 12:19:57.205 [GroupItemStateChangedEvent] - gThermostatMode changed from 1 to 0 through BS_Office_Thermostat_Mode
2020-10-26 12:19:57.208 [vent.ItemStateChangedEvent] - BS_Office_Thermostat_Mode changed from 1 to 0
2020-10-26 12:19:58.868 [vent.ItemStateChangedEvent] - BS_Office_Thermostat_Valve changed from 36 to 0

2020-10-26 12:20:13.372 [vent.ItemStateChangedEvent] - BS_Office_Window changed from OPEN to CLOSED
==> /var/log/openhab2/openhab.log <==
2020-10-26 12:20:13.397 [INFO ] [smarthome.model.script.WINDOW OFFICE] - Temp Var: 0
==> /var/log/openhab2/events.log <==
2020-10-26 12:20:13.403 [GroupItemStateChangedEvent] - gThermostatMode changed from 0 to 1 through BS_Office_Thermostat_Mode
2020-10-26 12:20:13.406 [vent.ItemStateChangedEvent] - BS_Office_Thermostat_Mode changed from 0 to 1
==> /var/log/openhab2/openhab.log <==
2020-10-26 12:20:13.408 [INFO ] [smarthome.model.script.WINDOW OFFICE] - Window:CLOSED
2020-10-26 12:20:13.417 [INFO ] [smarthome.model.script.WINDOW OFFICE] - Therm Set Temp: 23
2020-10-26 12:20:13.424 [INFO ] [smarthome.model.script.WINDOW OFFICE] - Therm Mode:1

This is bad syntax. Items are not simple variables.

If you want to update an Items state, use yourItem.postUpdate().
If you want to issue a command (and so eventually command a linked real device), use yourItem.sendCommand(), just as you already doin the first part of the rule.

Thank you @rossko57
you’re right it reacts properly now with follwong change

        BS_Office_Thermostat_SetpointHeat.sendCommand (CurrentThermSetTemp)
        BS_Office_Thermostat_Mode.sendCommand (CurrentThermMode)

Looking also here
Shall I change varaibles to string instead of Number?

Is there any way to capture whole item settings before window will open and restore it 1:1 after it closes?
How can I introduce a period of time after reaction will take place?
When window open --> read all Thermostat values --> wait 2min --> set thermostat off
When window closed --> update Thermostat values from variable.

Regards,
Jacek

https://www.openhab.org/docs/configuration/actions.html#event-bus-actions is probably what you are looking for.

https://www.openhab.org/docs/configuration/actions.html#timers is what you are after. Search the forum for createTimer and you will find thousands of examples.

thank you @rlkoshak for your hints. I went through persistency of the Item, made some tests and stucked.

Goal: store all Item configuration and restore after condition occures.

My Thermostate Items:

// Thermostat SPIRIT
Dimmer                      BS_Office_Thermostat_Valve                               "Office Valve [%d %%]"                          <radiator>           (BS_Office)                                                    ["Valve"]                           {channel="zwave:device:8985008e:node6:switch_dimmer"}
Number                      BS_Office_Thermostat_Temp                                "Office Thermostat Temp. [%.1f °C]"             <temperature>        (BS_Office, gTemperature, gHeating)                            ["Themostat Temp."]                 {channel="zwave:device:8985008e:node6:sensor_temperature"}
Number                      BS_Office_Thermostat_Mode                                "Office Thermostat Mode"                        <settings>           (BS_Office, gThermostatMode)                                   ["Themostat Mode"]                  {channel="zwave:device:8985008e:node6:thermostat_mode"}
Number                      BS_Office_Thermostat_SetpointHeat                        "Office Thermostat Setpoint Heat [%.1f °C]"     <temperature>        (BS_Office, gHeating)                                          ["Set Point (heat)"]                {channel="zwave:device:8985008e:node6:thermostat_setpoint_heating"}
Number                      BS_Office_Thermostat_SetpointEHeat                       "Office Thermostat Setpoint EHeat [%.1f °C]"    <temperature>        (BS_Office, gHeating)                                          ["Set Point (EHeat)"]               {channel="zwave:device:8985008e:node6:thermostat_setpoint_heating_econ"}
Number                      BS_Office_Thermostat_Battery                             "Office Thermostat"                             <batterylevel>       (BS_Office, gHeating, gBattery)                                ["Battery"]                         {channel="zwave:device:8985008e:node6:battery-level"}
Switch                      BS_Office_Thermostat_AlarmSystem                         "Office Thermostat System [%s]"                 <alarm>              (BS_Office, gHeating, gAlarm)                                  ["Alarm System"]                    {channel="zwave:device:8985008e:node6:alarm_system"}
Switch                      BS_Office_Thermostat_AlarmPower                          "Office Thermostat Power [%s]"                  <alarm>              (BS_Office, gHeating, gAlarm)                                  ["Alarm Power"]                     {channel="zwave:device:8985008e:node6:alarm_power"}

//Test button
Switch    TestButton               "test button"               <switch>

My rule:

import java.util.Map
val Map ThermostatOfficePresistency = null
 
rule "Test"
when
    Item TestButton received update

then 
    if (TestButton.state == ON){
        ThermostatOfficePresistency = storeStates(BS_Office_Thermostat_Valve, BS_Office_Thermostat_Temp, BS_Office_Thermostat_Mode, BS_Office_Thermostat_SetpointHeat, BS_Office_Thermostat_SetpointEHeat)
        logInfo("test:", "Thermostat Persistency (ON): " + ThermostatOfficePresistency )
    }

    else if
        (TestButton.state == OFF){
        logInfo("test:", "Thermostate Mode " + BS_Office_Thermostat_Mode )
        restoreStates(BS_Office_Thermostat_Valve, BS_Office_Thermostat_Temp, BS_Office_Thermostat_Mode, BS_Office_Thermostat_SetpointHeat, BS_Office_Thermostat_SetpointEHeat)
        logInfo("test:", "Thermostat Mode after restore: " + BS_Office_Thermostat_Mode )
    }
end 

My intention is

  • when switch is ON -> Items and their states are going to be stored -> this works perfectly
  • after values stored I am changing the state of TempSet manualy and check it in the log before restoring items. Results ov the manual change and later restoring shall be visible in logs.

I do sth wrong with restore states. I am not sure if I can write this way. I assume there is an error between defined variables.

Logfile:

2020-10-28 18:09:25.246 [vent.ItemStateChangedEvent] - TestButton changed from OFF to ON
==> /var/log/openhab2/openhab.log <==
2020-10-28 18:09:27.526 [INFO ] [eclipse.smarthome.model.script.test:] - Thermostat Persistency (ON): {BS_Office_Thermostat_Temp (Type=NumberItem, State=20.16, Label=Office Thermostat Temp., Category=temperature, Tags=[Themostat Temp.], Groups=[BS_Office, gTemperature, gHeating])=20.16, BS_Office_Thermostat_Valve (Type=DimmerItem, State=16, Label=Office Valve, Category=radiator, Tags=[Valve], Groups=[BS_Office])=16, BS_Office_Thermostat_Mode (Type=NumberItem, State=1, Label=Office Thermostat Mode, Category=settings, Tags=[Themostat Mode], Groups=[BS_Office, gThermostatMode])=1, BS_Office_Thermostat_SetpointHeat (Type=NumberItem, State=19, Label=Office Thermostat Setpoint Heat, Category=temperature, Tags=[Set Point (heat)], Groups=[BS_Office, gHeating])=19, BS_Office_Thermostat_SetpointEHeat (Type=NumberItem, State=19, Label=Office Thermostat Setpoint EHeat, Category=temperature, Tags=[Set Point (EHeat)], Groups=[BS_Office, gHeating])=19}
==> /var/log/openhab2/events.log <==
2020-10-28 18:09:29.434 [ome.event.ItemCommandEvent] - Item 'TestButton' received command OFF
2020-10-28 18:09:29.458 [vent.ItemStateChangedEvent] - TestButton changed from ON to OFF
==> /var/log/openhab2/openhab.log <==
2020-10-28 18:09:29.475 [INFO ] [eclipse.smarthome.model.script.test:] - Thermostate Mode BS_Office_Thermostat_Mode (Type=NumberItem, State=1, Label=Office Thermostat Mode, Category=settings, Tags=[Themostat Mode], Groups=[BS_Office, gThermostatMode])
2020-10-28 18:09:29.487 [ERROR] [ntime.internal.engine.RuleEngineImpl] - Rule 'Test': An error occurred during the script execution: Could not invoke method: org.eclipse.smarthome.model.script.actions.BusEvent.restoreStates(java.util.Map) on instance: null

This needs telling where to restore from.

You need to look at the docs again for how storeStates and restoreStates work. You call storeStates which returns a Map which gets stored in ThermostatOfficePersistency. You need to pass ThermostatOfficePersistency to restoreStates to return the Items back to the states that were stored.

I do my best belive me. The only official documentaion is this one you mentioned. I know for many couple of sentence written there is enough, for me difficult to understand without examples.

I made modification after reading the doc again and some posts: this one and this one
I made correction regarding the @rossko57 comment:

This needs telling where to restore from.

    logInfo("test:", "Thermostate Mode " + BS_Office_Thermostat_Mode )
    ThermostatOfficePresistency2 = restoreStates(ThermostatOfficePresistency)
    logInfo("test:", "Thermostat Mode after restore: " + BS_Office_Thermostat_Mode2 )

and valueas are restored but there is still sth. wrong.

 2020-10-28 19:01:00.472 [ERROR] [ntime.internal.engine.RuleEngineImpl] - Rule 'Test': An error occurred during the script execution: Couldn't invoke 'assignValueTo' for feature JvmVoid:  (eProxyURI: thermostats.rules#|::0.2.0.2.0.0.2.1.0.1::0::/1)

Log:

2020-10-28 18:46:17.371 [ome.event.ItemCommandEvent] - Item 'TestButton' received command ON
2020-10-28 18:46:17.386 [vent.ItemStateChangedEvent] - TestButton changed from OFF to ON
==> /var/log/openhab2/openhab.log <==
2020-10-28 18:46:17.401 [INFO ] [eclipse.smarthome.model.script.test:] - Thermostat Persistency (ON): {BS_Office_Thermostat_Temp (Type=NumberItem, State=19.81, Label=Office Thermostat Temp., Category=temperature, Tags=[Themostat Temp.], Groups=[BS_Office, gTemperature, gHeating])=19.81, BS_Office_Thermostat_Valve (Type=DimmerItem, State=15, Label=Office Valve, Category=radiator, Tags=[Valve], Groups=[BS_Office])=15, BS_Office_Thermostat_Mode (Type=NumberItem, State=1, Label=Office Thermostat Mode, Category=settings, Tags=[Themostat Mode], Groups=[BS_Office, gThermostatMode])=1, BS_Office_Thermostat_SetpointHeat (Type=NumberItem, State=19, Label=Office Thermostat Setpoint Heat, Category=temperature, Tags=[Set Point (heat)], Groups=[BS_Office, gHeating])=19, BS_Office_Thermostat_SetpointEHeat (Type=NumberItem, State=19, Label=Office Thermostat Setpoint EHeat, Category=temperature, Tags=[Set Point (EHeat)], Groups=[BS_Office, gHeating])=19}
==> /var/log/openhab2/events.log <==

2020-10-28 18:47:08.259 [ome.event.ItemCommandEvent] - Item 'BS_Office_Thermostat_Mode' received command 0
2020-10-28 18:47:08.291 [nt.ItemStatePredictedEvent] - BS_Office_Thermostat_Mode predicted to become 0
2020-10-28 18:47:08.319 [vent.ItemStateChangedEvent] - BS_Office_Thermostat_Mode changed from 1 to 0
2020-10-28 18:47:09.984 [vent.ItemStateChangedEvent] - BS_Office_Thermostat_Valve changed from 15 to 0

2020-10-28 18:48:03.808 [ome.event.ItemCommandEvent] - Item 'TestButton' received command OFF
2020-10-28 18:48:03.821 [vent.ItemStateChangedEvent] - TestButton changed from ON to OFF
==> /var/log/openhab2/openhab.log <==
2020-10-28 18:48:03.840 [INFO ] [eclipse.smarthome.model.script.test:] - Thermostate Mode BS_Office_Thermostat_Mode (Type=NumberItem, State=0, Label=Office Thermostat Mode, Category=settings, Tags=[Themostat Mode], Groups=[BS_Office, gThermostatMode])
==> /var/log/openhab2/events.log <==
2020-10-28 18:48:03.850 [ome.event.ItemCommandEvent] - Item 'BS_Office_Thermostat_Temp' received command 19.81
==> /var/log/openhab2/openhab.log <==

2020-10-28 18:48:03.852 [ERROR] [ntime.internal.engine.RuleEngineImpl] - Rule 'Test': An error occurred during the script execution: Couldn't invoke 'assignValueTo' for feature JvmVoid:  (eProxyURI: thermostats.rules#|::0.2.0.2.0.0.2.1.0.1::0::/1)

==> /var/log/openhab2/events.log <==
2020-10-28 18:48:03.884 [ome.event.ItemCommandEvent] - Item 'BS_Office_Thermostat_Valve' received command 15
2020-10-28 18:48:03.915 [ome.event.ItemCommandEvent] - Item 'BS_Office_Thermostat_Mode' received command 1
2020-10-28 18:48:03.936 [ome.event.ItemCommandEvent] - Item 'BS_Office_Thermostat_SetpointHeat' received command 19
2020-10-28 18:48:03.954 [ome.event.ItemCommandEvent] - Item 'BS_Office_Thermostat_SetpointEHeat' received command 19
2020-10-28 18:48:03.971 [nt.ItemStatePredictedEvent] - BS_Office_Thermostat_Temp predicted to become 19.81
2020-10-28 18:48:03.985 [nt.ItemStatePredictedEvent] - BS_Office_Thermostat_Valve predicted to become 15
2020-10-28 18:48:03.999 [nt.ItemStatePredictedEvent] - BS_Office_Thermostat_Mode predicted to become 1
2020-10-28 18:48:04.014 [nt.ItemStatePredictedEvent] - BS_Office_Thermostat_SetpointHeat predicted to become 19
2020-10-28 18:48:04.036 [nt.ItemStatePredictedEvent] - BS_Office_Thermostat_SetpointEHeat predicted to become 19
2020-10-28 18:48:04.048 [vent.ItemStateChangedEvent] - BS_Office_Thermostat_Valve changed from 0 to 15
2020-10-28 18:48:04.050 [GroupItemStateChangedEvent] - gThermostatMode changed from 0 to 1 through BS_Office_Thermostat_Mode
2020-10-28 18:48:04.052 [vent.ItemStateChangedEvent] - BS_Office_Thermostat_Mode changed from 0 to 1

Is that an Item there? You would be interested in its .state, I expect

You can treat this as though it were a “function”, just -

restoreStates(ThermostatOfficePresistency)

Is that some other Item we have not seen before? It is different from the one in the first logInfo.
Again, if it is an Item it is probably the .state that you want.

I have doubts that the log will work as you expect anyway; I think the restore issues updates to Items. Update requests are asynchronous, and take a few milliseconds. Your rule will not stop and wait for those to complete, so you’ll most likely log out the "old’ state.
You’ll see if it worked in your events.log if you are expecting a change in state.

This item was only temporary to see what will be the overall Item status. You’re right I was intersted in its state.

It works perfeact, thank you. I was tryinig to see the status od this what was stored, but you are also right. It is visible in log files anyway.

Yes. I was playing meantime with rule and wanted to trace some activites and states.

I removed logging this way. Status is beiong logged anyway after restoring values.

My final rule looks as follow:

import java.util.Map
var Number TempVar
val Map ThermostatOfficePresistency = null


rule "Action on Window open in Office"

when
    Item BS_Office_Window changed
then
    // If Window is open and Theremostat is NOT OFF. If Thermostat is OFF no influence on it while window is open
       
    if ((BS_Office_Window.state == OPEN) && (BS_Office_Thermostat_Mode.state != 0)) {
        TempVar = 1                                     // remember previous window status. Is open
        ThermostatOfficePresistency = storeStates(BS_Office_Thermostat_Valve, BS_Office_Thermostat_Temp, BS_Office_Thermostat_Mode, BS_Office_Thermostat_SetpointHeat, BS_Office_Thermostat_SetpointEHeat)
        logInfo("Thermostat Office:", "Thermostat Settings stored: " + ThermostatOfficePresistency )
        BS_Office_Thermostat_Mode.sendCommand (0)       // Close thermostat in the office
    }

    // If window is closed and previously was opened.
    if ((BS_Office_Window.state == CLOSED) && (TempVar == 1)) {  

        restoreStates(ThermostatOfficePresistency)
        logInfo("Thermostat Office:", "Check Thermostat Mode after restore" )
        TempVar = 0
    }
end

At the moment the rule is beeing saved follwoing worning is beeing shown.
What should be the proper declaration of the Value?

val Map ThermostatOfficePresistency = null
2020-10-29 09:27:57.792 [INFO ] [el.core.internal.ModelRepositoryImpl] - Validation issues found in configuration model 'thermostats.rules', using it anyway:

Map is a raw type. References to generic type Map<K, V> should be parameterized
Assignment to final field
Assignment to final field

2020-10-29 09:27:57.847 [INFO ] [el.core.internal.ModelRepositoryImpl] - Refreshing model 'thermostats.rules'

First of all the log message is just informational, not an error. So there is nothing really wrong with the declaration. However, it is usually best to give the Map what types the key and value will be. The docs show that storeStates returns a Map<Item, State>. However, I don’t think you can reference Item from inside a Rules DSL rule. If it doesn’t work you can try GenericItem instead. If that doesn’t work you can just ignore the warning.

However, there are two other warnings there that do have to dealt with and I’m surprised they aren’t errors.

val is a constant. You cannot change the value of a variable defined with val. In the terms of the warning, val is final. You need to declare TempVar and ThermostatOfficePresistency using var, not val because you reassign the variable later inside the rule.

I tried several ways to define that properly no succes. Every combination with two parameters i wanted to assign to my variable ended with errors or warnings. I left is without parameters hoping the openhabian assigned the right one.

var Map ThermostatOfficePresistency
var TempVar

I like to have code without warnings. and this does not let me sleep :wink:

I decided to extend my rule with following functionality.
All Thermostats shall be switched off after 23.30 and switched on at 5.30 to their previous states.
All of thermostats are in the gThermostatMode group.
Can I store states from all Items in a group? The test example below does not work, providing an error below.

var Map ThermostatAllPresistency

rule "TEST"
when
    Item TestButton changed
then
    if (TestButton.state == ON) {
    ThermostatAllPresistency = storeStates(gThermostatMode)
    logInfo("TEST", "ThermostatALL: GO SLEEP")
    logInfo("TEST", "ThermostatALL: " + ThermostatAllPresistency )

    }

    if (TestButton.state == OFF) {
    logInfo("TEST", "ThermostatALL: WAKE UP"  )
    }
    
end
2020-11-21 16:10:58.907 [ERROR] [ntime.internal.engine.RuleEngineImpl] - Rule 'TEST': An error occurred during the script execution: Could not invoke method: org.eclipse.xtext.xbase.lib.ObjectExtensions.operator_plus(java.lang.Object,java.lang.String) on instance: null

The error is complaining about the plus operator and the only place that occurs in the rule is the second logInfo line. It can’t convert ThermostatAllPresistency to a String. So try adding .toString and see if that helps. At a minimum the error should change which can be informative.

The perstistency rule for all thermostat heads works, logging does not.
I think I can leave without seeing the message and status of all Items in the log. I see thermostat Items being updated, it shall be enough.

Thank you @rlkoshak for your help.

rule "TEST"
when
    Item TestButton changed
then
    if (TestButton.state == ON) {
    ThermostatAllPresistency = storeStates(gThermostatMode)
    gThermostatMode.sendCommand(0)
    logInfo("TEST", "ThermostatALL: GO SLEEP")
    logInfo("TEST", "ThermostatALL: " +ThermostatAllPresistency.toString )

    }

    if (TestButton.state == OFF) {
    restoreStates(ThermostatAllPresistency)
    logInfo("TEST", "ThermostatALL: Previous Thermostat settings restored")
    }
    
end
2020-11-23 12:24:44.013 [ERROR] [ntime.internal.engine.RuleEngineImpl] - Rule 'TEST': 'toString' is not a member of 'Object'; line 26, column 40, length 33

All Item state changes get logged to events.log.

As I indicated, the error would be different with the change which is informative. For some reason ThermostatAllPresistency is not a Map but just a generic Object. But even an Object should have a toString. Perhaps if the result of storeStates is cast to a Map it would work.