Variable scope in rules

Think i might be missing something small.

I created a rule to allow max volume on the TV based on the “mood” the house is set to.

I can’t figure out why the statement below works depending on the position in the code. I don’t have both statements in the rule at the same time , I moved them up or down

   if(volumeMax > volume)  
   {
     volume = volumeMax
   }

If i put it above the case statement it works
If i put it below the case statement it fails with:
“Could not invoke method: org.eclipse.xtext.xbase.lib.IntegerExtensions.operator_greaterThan(int,int) on instance: null”

If i don’t do the assignment to TV1_Volume.state it also works 100%
I tried different item types and widgets, but noting works.
Using HABPanel

rule "TV1_Volume_Set"
  when
     Item TV1_Volume changed
  then
    var volumeMax = 0
    var volume = 0
    volume = TV1_Volume.state     // Was not able to use TV1_Volume.state directly in the sendCommand

    if(volumeMax > volume) // if statement works if i put it here 
    {
      volume = volumeMax
    }

    logInfo("TV1_Volume","Changed")
    switch HouseMood.state
    {
      case "SLEEP":
      {
        logInfo("House_Mood","Sleepy")
        volumeMax = 25
      }
      case "AWAKE":
      {
        logInfo("House_mood","Awake")
        volumeMax = 35
      }
      default:
      {
        volumeMax = 30
      }
   }

   if(volumeMax > volume)  //If statement fails if i put it after the case statement 
   {
     volume = volumeMax
   }

   // Send Command to TV to update volume
    sendCommand(TV1_Volume_SET,volume)

end

This statement fails .

sendCommand(TV1_Volume_SET,TV1_Volume.state)

Doing it like this works

var volume = 0
volume = TV1_Volume.state 
sendCommand(TV1_Volume_SET,volume)

The easy problem first.

The sendCommand Action expects a String. Often, particularly when dealing with numerical Item Types, it is unable to automatically convert the numerical value to a String so the call to sendCommand(TV1_Volume_SET, TV1_Volume.state) will fail but calling the method like TV1_Volume_SET.sendCommand(TV1_Volume.state) will succeed. This is why I always recommend using the method instead of the Action where ever possible.

Alternatively, the following variants should work as well:

  • TV1_Volume_SET.sendCommand(TV1_Volume.state as Number)
  • 'sendCommand(TV1_Volume_SET, TV1_Volume.state.toString)`

Now for the harder problem.

First, replace your definition of volume with:

var volume = TV1_Volume.state as Number

This will force volume to be of type Number and make it compatible for comparisons with volumeMax. I don’t think that is your problem but it can’t hurt.

Try logging out volumeMax and volume after the switch statement. Do they log as null or do they have a value?

At this point with the information I have all I can say is this is very odd behavior. The problem isn’t one of scope or else you would get a different error (no such symbol or something like that). The problem is somewhere along the way volume or volumeMax are either becoming incompatible with each other (i.e. you cannot use the > operator) or soemthing like that.

One other thing you could try is to force the variables to be of type Number:

var Number voliumeMax = 0
var Number volume = TV1_Volume.state as Number
1 Like

Thanks, New to Java and use to programming in PHP so type casting throws me off :slight_smile:

Manage to connect to the Sony Bravia TV using TCP Binding. I found that it’s worth restarting the server when you add/remove items , or change the tcp.cfg file. Using this does not require you to pair your TV with openhab like when you use the XML style interface

What it does is send command to the TV and updates back if the remote was used. I also limit the max volume based on what the mood is in the house

Below is what I’ve done to get it to work.
Used: “Sony Simple IP Control Protocol for BRAVIA Version”

// items/tv.items
// Channels
String  TV1_Channel "Sony Bravia Channel Selector"

// Volume
Number TV1_Volume "Sony Bravia Volume Selector"

// IRC Commands
String TV1_IRCC "Sony Braiva IRCC"

// Direct TCP Binding. Send and receive on 1 Channel
String _TV1_TCP "Sony Bravia TCP " { tcp=">[*:192.168.1.105:20060:'REGEX((.*?))']"}
// rules/tv.rules
// Global
var String TV1_channelStore
var Number TV1_volumeStore

// Max on TV volume is controled by House Mood
rule "TV1_Volume_Set"
  when
//     Item TV1_Volume changed  or       // Duplicate IRCC call , but it sets back to mood
     Item TV1_Volume received command
  then
    var String VolumeHead = "*SCVOLU0000000000000"
    var String VolumeSend = ""

    if(TV1_volumeStore != TV1_Volume.state)
    {
      logInfo("TV_Volume_Set","Update Volume")
      if(TV1_Volume.state <= _TV_HouseMood.state )
      {
        logInfo("TV Volume ", ("000" + TV1_Volume.state.toString()).substring(TV1_Volume.state.toString().length()))
        VolumeSend = VolumeHead + ("000" + TV1_Volume.state.toString()).substring(TV1_Volume.state.toString().length())
        TV1_volumeStore = TV1_Volume.state;
        sendCommand(_TV1_TCP,VolumeSend)
      }
      else
      {
        VolumeSend = VolumeHead + ("000" + _TV_HouseMood.state.toString()).substring(_TV_HouseMood.state.toString().length())
        logInfo("TV Volume ",("000" + _TV_HouseMood.state.toString()).substring(_TV_HouseMood.state.toString().length()))
        TV1_volumeStore = _TV_HouseMood.state;
        sendCommand(_TV1_TCP,VolumeSend)
        postUpdate(TV1_Volume,_TV_HouseMood.state)
      }
    }
    else
    {
      logInfo("TV_Volume_Set","Update Ignored")
    }

end

// TV Channel Command Recevied
rule "TV1_Channel_Set"
  when
    Item TV1_Channel received command
  then
   var String ChannelHead = "*SCCHNN00000"
   var String ChannelTail   = ".0000000"
   var String ChannelSelect = ChannelHead + TV1_Channel.state + ChannelTail
   logInfo("TV1_Channel_Set","Command recevied")
   // Send Command to update channel
   if(receivedCommand != TV1_channelStore)
   {
     sendCommand(_TV1_TCP,ChannelSelect)
     TV1_channelStore = receivedCommand
   }
end

// IRCC Command Received
rule "TV1_IRCC_Set"
  when
    Item TV1_IRCC received command
  then
   var String ChannelHead = "*SCIRCC00000000000000"
   var String ChannelSelect = ChannelHead + TV1_IRCC.state
   sendCommand(_TV1_TCP,ChannelSelect)
end


// Responce received from TV
rule "TV1_Channel_Notify"
  when
    Item _TV1_TCP changed
  then
    var String messageType = _TV1_TCP.state.toString().substring(2,3)
    var String messageFunction = _TV1_TCP.state.toString().substring(3,7)
    var String messageParam    = _TV1_TCP.state.toString().substring(7)

    switch messageType
    {
      case "N": // Notify received
      {
        //logInfo("TV1_Channel_Notify",messageFunction)
        switch messageFunction
        {
          case "CHNN": // Channel changed
          {
            postUpdate(TV1_Channel,messageParam.substring(5,8))
          }
          case "VOLU": // Volume changed
          {
            var Number volume = Integer.parseInt(messageParam.substring(5))
            sendCommand(TV1_Volume, volume)
          }
        }
      }
      case "A": // Answer on command
      {
      }
    }

end
1 Like

Just to be clear, Rules are not written in Java. They are written in a Domain Specific Languaged based on the Xtend language.