How to deal with mixed (int/string) items?

All

I have a bunch of thermovalve (Thermotronic, z-wave) I control through a setpoint (allThermostatModes), a group (gThermoStatMode) and simple rules to switch between modes (off, economy, normal)

rule "Heat mode has changed"

when
    Item allThermostatModes received command
then
    val String ruleIdentifier = "Setting Heating System Mode"

    logInfo(ruleIdentifier, "Heating system set to {} mode!", allThermostatModes.state)

    gThermoStatMode.members.forEach[tv | 
        logInfo(ruleIdentifier, "Setting {} set to {} mode!", tv, allThermostatModes.state)
        tv.sendCommand((allThermostatModes.state as Number).intValue)
    
    ]

end

Everything works OK since a year :slight_smile:, each member directly map the operating mode into an int value (0=Off, 1=Default, 11=Economy), no need to use any profile or transformation on the channel.

I have now added another thermovalve (same company, but DECT based), and the operation modes are Strings (ON/OFF/COMFORT/ECO/BOOST/WINDOW_OPEN).

Needless to say, the rule above cannot work.

To my understanding Channel Profile settings will modify the output of the channel ahead of passing it to the Item, but how can I remap back the Int value from the rule back into the Item/thing?

I tried some “back-mapping” with “Command Options” but it did not seem to work.

I guess I could use some intermediate Item and rule to pass from integer to the string, but I wish there is a smarter way to do so.

For completeness, I use OH3.1 and I do not need to map all modes of the new thermovalve, I am happy if I can get the legacy one mapped.

Thanks for any hint!
Max

I’m confused. Are you saying that you map the numbers sent by the Channel to a String and then you need to map the numbers back to a String to command the device? Why not leave it alone instead of mapping and then remappping?

If that’s not feasible for some reason, use the Map transformation. You can use it at the Channel and in the rule using the transform Action to map between one value and another. But if the mapping is really only used for display purposes, just use the Map transform in your Item labels/State Descriptions. Then you will see the String in the UI but the Item remains a number.

@rlkoshak

Apologize to be unclear. Let me make clarify

  • legacy thermovalve (zwave): channel deliver numbers, command to be sent as integer
  • newly introduced thermovalve (DECT): channel delivers string, command to be sent as string

So, the basic question is: how can I control all thermovalves with a single rule? Should I use the transform action as shown in the doc?

In such a case, what is the most “elegant” way to implement it? Should I use some additional subgroups (e.g. zwave vs DECT) to trigger the transformation?

Thanks, you are already bringing me on the right path :smiley:

Max

If the two valves take different commands, you’ll need some way to identify which valve you are dealing with and transform the command as needed.

It’s a simple Map transformation to go from one to the other but you’ll need to know which direction you need to go on an Item by Item basis.

That could be done by putting them in different Groups, Item names, tags, or other ways.

@rlkoshak

thanks again for your input.

A possible solution is sketched below - I tested it under several scenarios and it seems to work. However, I am not happy to have an hardcoded tag in more files… I would be happy if anybody has a more elegant way to do what I did below. Otherwise I will mark this post as solution for future reference :wink:

I have now implemented the identification process through an additional tag - radiatorStringMode - and included this into my DSL rule as below


    gThermoStatMode.members.forEach[tv | 
        logInfo(ruleIdentifier, "Setting {} set to {} mode!", tv, allThermostatModes.state)

        var itemTags = tv.getTags
        if(  itemTags.contains("radiatorStringMode") ) {
            tv.sendCommand(transform("MAP", "thermostat2cometRadiatorMode.map", allThermostatModes.state.toString))
        }
        else
            tv.sendCommand((allThermostatModes.state as Number).intValue)
    ]

while my jython rule (used to turn the radiators off each time I open a window - I would like to move all rules to Jython in the future) has changed in

    for iName in thermostaticValvesList:

        item2cmd = itemRegistry.getItem(iName)
        
        if(itemRegistry.getItem(iName).getTags().contains("radiatorStringMode")):
            events.sendCommand(item2cmd, Transformation.transform("MAP", "thermostat2cometRadiatorMode.map", str(heatModeVal)))
        else:
            events.sendCommand(item2cmd, heatModeVal)

With thermostat2cometRadiatorMode.map

0=OFF
1=COMFORT
11=ECO
15=BOOST

A tag is a property on an Item. It’s no more or less hard coded than any other property of an Item like its label or its name or what Channel it’s linked to.

You could do the same by creating a Group Own and put all the map items into that Group but that’s even more work. You could use special names for those Items but then you have to parse the names. You can use item metadata but you’d have to use a different language entirely and metadata is even more involved than tags. You could use the semantic model but that is Groups and tags already.

I’m not sure what you mean by more elegant than just applying a tag.