Problem getting Item State

openHABian 3 on RPi4:

I am trying to make a rule using javascript that turns the HVAC off when the sliding door is left open. For testing purposes a light is in place of teh door sensor. I’m getting an error on line 21 that “ReferenceError: “Switch_Hall_Light” is not defined in at line number 21”

Line 21:

if (Switch_Hall_Light.state == On) {

Full script:

var logger = Java.type("org.slf4j.LoggerFactory").getLogger("org.openhab.model.script.Rules.Slider HVAC Off");
var ScriptExecution = Java.type("org.openhab.core.model.script.actions.ScriptExecution");
logger.info(" Slider HVAC Off Rule Started");

var ZonedDateTime = Java.type("java.time.ZonedDateTime"); 
var now = ZonedDateTime.now();

var runme = function() {
    logger.info("Ecobee Mode Off");
  
function turnOff() {
    events.sendCommand("Ecobee_Mode", off);
    logger.info("Ecobee_Mode turned"+items["Ecobee_Mode"]);
    this.timer = undefined;
    }
  
    this.timer = undefined; // reset the timer back to undefined
}
logger.info("this.timer = " + this.timer);

if (Switch_Hall_Light.state == On) {
     if(this.timer === undefined) {
        this.timer = ScriptExecution.createTimer(now.plusSeconds(60), runme);
    }
}
else if (Switch_Hall_Light.state == Off) {
    if(this.timer !== undefined) {
        this.timer.cancel();
    }
}
logger.info("this.timer = " + this.timer)

Only DSL rules have access to the items directly via their names. In order to get items in the other script languages, you need to use the items registry:

ir.Switch_Hall_Light.getState()

But there’s an even more useful shortcut. Item states are also stored in an items dict to be accessed directly:

items.Switch_Hall_Light

And then the predefined states for a Switch type Item are ON and OFF

Thanks for the responses, after a few tries it appears that the script tries to change the Ecobee mode to Off, but I have two more problems. The log says that it turns the Ecobee mode off but it does not. And if I turn the hall light off during the run, the timer doesn’t cancel like it should and the script still tries to turn the Ecobee off.

I made another rule just to change the Ecobee mode to Off and it works so I know that part is functional. I also use this timer script to send/not send an email based on humidity readings staying elevated and last I check that was working so THAT part should be functional as well.

Here is the section that should change the Ecobee mode to Off and for the timer (note that the Ecobee command needs to be lower case):

function turnOff() {
    events.sendCommand("Ecobee_Mode", off);
    logger.info("Ecobee_Mode turned"+items["Ecobee_Mode"]);
    this.timer = undefined;
    }
  
    this.timer = undefined; // reset the timer back to undefined
}
logger.info("this.timer = " + this.timer);

if (items.Switch_Hall_Light == ON) {
     if(this.timer === undefined) {
        this.timer = ScriptExecution.createTimer(now.plusSeconds(60), runme);
    }
}
else if (items.Switch_Hall_Light == OFF) {
    if(this.timer !== undefined) {
        this.timer.cancel();
    }
}
logger.info("this.timer = " + this.timer)

There are no lowercase predefined commands. Did you mean to send a string “off”?

Aha! I don’t know what sending a string means but I’m assuming the command only works with predefined commands and a string is text that’s not predefined? So would it be events.sendString?

events.sendCommand is correct, you just need to give that function inputs that it can recognize. If you are sending a defined OH state, then these are built in to the script language as ON or OFF (all caps) if the item that you are commanding requires a string as the command then, to let the script engine know what you are sending is a string you must put it in quotes. Your version

events.sendCommand("Ecobee_Mode", off);

tells the engine to send the variable off as the command, but there’s no such thing as the variable off. With quotes:

events.sendCommand("Ecobee_Mode", "off");

You are now sending the "off" string.

By the by, don’t expect the command to have any effect by the time you log out the Item state in the next line. If it’s going to do anything at all it takes a finite time.

Understand rossko57, thanks.

Hmm, still not working:

var runme = function() {
    logger.info("Ecobee Mode Off");
  
function turnOff() {
    events.sendCommand("Ecobee_Mode", "off");
    logger.info("Ecobee_Mode turned"+items["Ecobee_Mode"]);
    this.timer = undefined;
    }

For a reality check, this simple GUI rule is working:

triggers: []
conditions: []
actions:
  - inputs: {}
    id: "1"
    configuration:
      itemName: Ecobee_Mode
      command: off
    type: core.ItemCommandAction

Can you expand on that, any messaging at all?

I don’t see anything that calls your turnOff function.

Do you mean a trigger for the rule? I’m running it manually for testing.

Logs:

2021-12-01 12:05:04.790 [INFO ] [b.model.script.Rules.Slider HVAC Off] -  Slider HVAC Off Rule Started
2021-12-01 12:05:04.831 [INFO ] [b.model.script.Rules.Slider HVAC Off] - this.timer = undefined
2021-12-01 12:05:04.881 [INFO ] [b.model.script.Rules.Slider HVAC Off] - this.timer = org.openhab.core.model.script.internal.actions.TimerImpl@1bac814
2021-12-01 12:06:04.838 [INFO ] [b.model.script.Rules.Slider HVAC Off] - Ecobee Mode Off

Inside your runme() function, you define another function turnOff(). I don’t know why. But I don’t see anything that asks for turnOff to be executed.

Ah, I see. I thought that’s what this was:

events.sendCommand("Ecobee_Mode", "off");

No, that does not call a function named turnOff().
If it this line is executed, it will command Item Ecobee_Mode with "off".
But that line is never executed.
Because it is inside a function called turnOff().
And nowhere in your code asks for function turnOff() to be executed.

function xxx() {
    blah;
    bleh;
    }

This defines a function called xxx.
Nothing else.
blah and bleh are not executed.

xxx();

This cause your previously defined function to be executed.
Now blah is executed.

1 Like

That makes sense. And it works now except that it turns teh mode to off even if I turn the hall light off during the timer run.

I have another script that sends an email based on humidity which drops if teh RH rises again and that works so I assume there is another problem with these lines?

if (items.Switch_Hall_Light == ON)

and this:

else if (items.Switch_Hall_Light == OFF)

Full script:

var logger = Java.type("org.slf4j.LoggerFactory").getLogger("org.openhab.model.script.Rules.Slider HVAC Off");
var ScriptExecution = Java.type("org.openhab.core.model.script.actions.ScriptExecution");
logger.info(" Slider HVAC Off Rule Started");

var ZonedDateTime = Java.type("java.time.ZonedDateTime"); 
var now = ZonedDateTime.now();

var runme = function() {
    logger.info("Ecobee Mode Off");
  
  events.sendCommand("Ecobee_Mode", "off");
  
    this.timer = undefined; // reset the timer back to undefined
}
logger.info("this.timer = " + this.timer);

if (items.Switch_Hall_Light == ON) {
     if(this.timer === undefined) {
        this.timer = ScriptExecution.createTimer(now.plusSeconds(60), runme);
    }
}
else if (items.Switch_Hall_Light == OFF) {
    if(this.timer !== undefined) {
        this.timer.cancel();
    }
}
logger.info("this.timer = " + this.timer)

No it isn’t. What are the triggers for your rule? This has a bearing on what conditions you might expect to find inside it.
The rule GUI helpfully provides a ‘code’ tab and the results of that would show everything we’d like to know.

If you think there’s a problem with that, find out -

logger.info("item state = " + items.Switch_Hall_Light);
if (items.Switch_Hall_Light == ON)

I’m not sure that this should rather be items["Switch_Hall_Light"] to get the state in a rule context though.

If you think there is a problem with evaluation, find out

if ( something ) {
    logger.info("something was true");
    more code
    }
else if ( something else ) {
    logger.info("something else was true");
    more code
    }
else  {
    logger.info("was not expecting that");
    }

Both formats should work in the UI rules and scripts.

if (items.Switch_KitchenLight_OnOff == ON) {
  logger.info("'.' format works")
}

if (items["Switch_KitchenLight_OnOff"] == ON) {
  logger.info("[] format works")
}

gives you

[INFO ] [org.openhab.rule.JSTester] - '.' format works

[INFO ] [org.openhab.rule.JSTester] - [] format works

So, indeed, this means there’s something wrong with the state of the item at the time the rule runs or the rule is not triggering as expected.

rossko57
No it isn’t. What are the triggers for your rule? This has a bearing on what conditions you might expect to find inside it.
The rule GUI helpfully provides a ‘code’ tab and the results of that would show everything we’d like to know.

I was triggering the rule manually because I thought the trigger would not have an impact on how the script ran. I have now created a trigger from turning teh hall light on.

rossko57
If you think there is a problem with evaluation, find out

Interesting. I added the logging and I can see teh log for when the switch is turned on, but not when it’s turned off (about 10 seconds later):

2021-12-02 11:04:39.705 [INFO ] [b.model.script.Rules.Slider HVAC Off] -  Slider HVAC Off Rule Started
2021-12-02 11:04:39.712 [INFO ] [b.model.script.Rules.Slider HVAC Off] - this.timer = undefined
2021-12-02 11:04:39.719 [INFO ] [b.model.script.Rules.Slider HVAC Off] - Switch_Hall_Light is ON
2021-12-02 11:04:39.722 [INFO ] [b.model.script.Rules.Slider HVAC Off] - this.timer = org.openhab.core.model.script.internal.actions.TimerImpl@248ec
==> /var/log/openhab/events.log <==
2021-12-02 11:04:39.685 [INFO ] [openhab.event.ItemCommandEvent      ] - Item 'Switch_Hall_Light' received command ON
2021-12-02 11:04:39.689 [INFO ] [penhab.event.ItemStatePredictedEvent] - Item 'Switch_Hall_Light' predicted to become ON
2021-12-02 11:04:39.695 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'Switch_Hall_Light' changed from OFF to ON
2021-12-02 11:04:54.403 [INFO ] [openhab.event.ItemCommandEvent      ] - Item 'Switch_Hall_Light' received command OFF
2021-12-02 11:04:54.406 [INFO ] [penhab.event.ItemStatePredictedEvent] - Item 'Switch_Hall_Light' predicted to become OFF
2021-12-02 11:04:54.413 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'Switch_Hall_Light' changed from ON to OFF
==> /var/log/openhab/openhab.log <==
2021-12-02 11:05:39.713 [INFO ] [b.model.script.Rules.Slider HVAC Off] - Ecobee Mode Off
==> /var/log/openhab/events.log <==
2021-12-02 11:05:39.723 [INFO ] [openhab.event.ItemCommandEvent      ] - Item 'Ecobee_Mode' received command off
2021-12-02 11:05:39.725 [INFO ] [penhab.event.ItemStatePredictedEvent] - Item 'Ecobee_Mode' predicted to become off
2021-12-02 11:05:39.729 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'Ecobee_Mode' changed from cool to off

Here is the entire script:

triggers:
  - id: "2"
    configuration:
      itemName: Switch_Hall_Light
      state: ON
    type: core.ItemStateChangeTrigger
conditions: []
actions:
  - inputs: {}
    id: "1"
    configuration:
      type: application/javascript
      script: >-
        var logger =
        Java.type("org.slf4j.LoggerFactory").getLogger("org.openhab.model.script.Rules.Slider
        HVAC Off");

        var ScriptExecution = Java.type("org.openhab.core.model.script.actions.ScriptExecution");

        logger.info(" Slider HVAC Off Rule Started");


        var ZonedDateTime = Java.type("java.time.ZonedDateTime"); 

        var now = ZonedDateTime.now();


        var runme = function() {
            logger.info("Ecobee Mode Off");
          
          events.sendCommand("Ecobee_Mode", "off");
          
            this.timer = undefined; // reset the timer back to undefined
        }

        logger.info("this.timer = " + this.timer);


        if (items.Switch_Hall_Light == ON) {
             if(this.timer === undefined) {
                this.timer = ScriptExecution.createTimer(now.plusSeconds(60), runme);
            }
        logger.info("Switch_Hall_Light is ON");
          
        }

        else if (items.Switch_Hall_Light == OFF) {
            if(this.timer !== undefined) {
                this.timer.cancel();
            }
        logger.info("Switch_Hall_Light is OFF");
          
        }

        logger.info("this.timer = " + this.timer)
    type: script.ScriptAction

Okay.

Okay, that’s doing what you asked it to do.

Did you mean to trigger from any state change?

It should cancel the timer if the light is turned off while the timer is running. I hesitate to guess since I have such a hard time understanding javascript but it seems like the problem is that it will only cancel the timer if it’s undefined, but while it’s running it IS defined.

else if (items.Switch_Hall_Light == OFF) {
    if(this.timer !== undefined) {
        this.timer.cancel();