Help with better HVAC rule design

I have a rule that I am copying and pasting lots of times as part of my HVAC control. The rule looks like this:

  // Heat Office
  if (Office_Tstat_HeatSetPoint.state > Office_Tstat_Temperature.state) {
    if (Office_Tstat_Temperature.state > Basement_HVAC_Supply.state) {
      sendCommand(Office_Damper, ON)
    } else {
      sendCommand(Office_Damper, OFF)
    }
  // Cool Office
  } else if (Office_Tstat_CoolSetPoint.state < Office_Tstat_Temperature.state) {
    if (Office_Tstat_Temperature.state < Basement_HVAC_Supply.state) {
      sendCommand(Office_Damper, ON)
    } else {
      sendCommand(Office_Damper, OFF)
    }
  } else {
    sendCommand(Office_Damper, ON)
  }

And “Office” as an example could be “Shop”, “Water_Room”, “Basement_Storage”, “Electric_Room”, or “Main_Basement”. I have a group that has all my room names and I know from @rlkoshak that I can do something like this:

 Basement_Rooms.members.forEach[ room |
    logInfo("HVAC","Room: " + room.name)
  ]

What I am struggling with is how to use the room.name string to form all the item names I need. As an example I tried:

if (room.name + "_Tstat_HeatSetPoint.state" > room.name + "_Tstat_Temperature.state") {

And learned you can’t combine like that.

1 Like

if ((room.name + “_Tstat_HeatSetPoint.state”) > (room.name + “_Tstat_Temperature.state”)) {

Works!

The full writeup is Design Pattern: Associated Items.

The tl;dr is you need to name the Items such that you can construct the name of an associated Item using the name of the Item you have. This may require some String manipulation, usually a split("_"). With the name, you can pull the Item out of a Group using a filter or find first. So it would look something like:

Basement_Rooms.members.forEach[ room |
    var setpoint = HVAC_Items.members.findFirst[ room.name+"_Tstat_HeatSetPoint" ] as NumberItem
    var temp= HVAC_Items.members.findFirst[ room.name +"_Tstat_Temperature" ] as NumberItem
    var damper = HVAC_Items.members.findFrist[ room.name+"_Damper" ] as SwitchItem
    damper.sendCommand(if(setpoint.state > temp.state) ON else OFF)
]

I suspect that was a fluke. The String “Office_Tstat_HeatSetPoint.state” comes before “Office_Tstat_Temperature.state” alphabetically and so > returns false instead of an error.

Makes sense, thanks!

  Basement_Rooms.members.forEach[ room |
    logInfo("HVAC","Room: " + room.name)
    var HeatSetPoint = Thermostats.members.findFirst[ room.name+"_Tstat_HeatSetPoint" ] as NumberItem
    logInfo("HVAC", "var: " + HeatSetPoint)
  ]

zwave-thermostats.items:Number Basement_Tstat_HeatSetPoint "Basement Heat Setpoint [%.1f °F]" <temperature> (Tstat10, Foo) {channel="zwave:rtc_ct100_00_000:controller:node19:thermostat_setpoint_heating"}

The error I get is:

2018-04-03 16:13:40.006 [ERROR] [ntime.internal.engine.ExecuteRuleJob] - Error during the execution of rule 'Testing': java.lang.String cannot be cast to java.lang.Boolean

My mistake and I made it three times. Ugh.

var setpoint = HVAC_Items.members.findFirst[ sp | sp.name == room.name+"_Tstat_HeatSetPoint" ] as NumberItem
var temp= HVAC_Items.members.findFirst[ t | t.name == room.name +"_Tstat_Temperature" ] as NumberItem
var damper = HVAC_Items.members.findFrist[ d | d.name == room.name+"_Damper" ] as SwitchItem

Use the DP post as a guide over my quick and dirty (and apparently wrong) example above.

That gives me null even tho there is a member named room.name+_Tstat_HeatSetPoint.

I have been looking at the DP post and I don’t see much except:

var actuator = C_Heat_Actuators.members.findFirst[name.equals(valve.name + “_Switch”)]

So I tried:
var HeatSetPoint = Thermostats.members.findFirst[name.equals (room.name+"_Tstat_HeatSetPoint") ] as NumberItem

And that also is null.

Log out room.name+"_Tstat_HeatSetPoint" and verify that it looks right.

Log out all the members of the group to verify that the Item is indeed a member of the Group.