MQTT: Combine multiple Channels of one thing before sending

I’m currently setting up my NSPanel flashed with Tasmota and I want to set the temperature which is displayed on the main screen independently of the displayed weather icon and the forecast range.
However it is required to send one JSON object to a topic which contains all 4 values.
What I want to achieve is that if one of the 4 is updated, one JSON payload will be send to the topic.
My initial thought was that JS Transformation might be the way to go, but it will only receive one of the 4 states, not all of them.

This is my thing:

Bridge mqtt:broker:mosquitto [
  //...
] {
    Thing topic livingroomNsPanel "Livingroom NsPanel" @ "Livingroom" [
      availabilityTopic="livingroom/nspanel/LWT",
      payloadAvailable="Online",
      payloadNotAvailable="Offline"
    ] {
    Channels:
      Type string : screenMainWeatherIcon [
        commandTopic="livingroom/nspanel/cmnd/nspsend",
        transformationPatternOut="???",
        allowedStates="sunny,partlyCloudy,cloudy,fog,storm,lightSnow,snow,hail,softHail,redThermostat,blueThermostat,windy,rainy"
      ]
      Type number : sreenMainOutdoorTemperature [
        commandTopic="livingroom/nspanel/cmnd/nspsend",
        transformationPatternOut="???"
      ]
  }
}

The JSON being send out must look like this:

{"HMI_weather":7,"HMI_outdoorTemp":{"current":5,"range":"-3,8"}}

If I had all values available, the JS Transformation would probably look like this:

/**
 * 
 * @param {string} icon 
 * @param {number} temperature
 * @param {number} min
 * @param {number} max
 * @returns string
 */
function (icon, temperature, min, max) {
    const iconMap = {
        "sunny": 1,
        "partlyCloudy": 2,
        "cloudy": 7,
        "fog": 11,
        "storm": 15,
        "lightSnow": 20,
        "snow": 22,
        "hail": 24,
        "softHail": 29,
        "redThermostat": 30,
        "blueThermostat": 31,
        "windy": 32,
        "rainy": 40
    };

    const result = {
        "HMI_weather": iconMap[icon],
        "HMI_outdoorTemp": {
            "current": temperature,
            "range": `${min}-${max}`
        }
    };

    return result;
}

But how do I get all the different states if only one is updated?

Instead of a transformation, use a rule to build the string from item states. It won’t matter if the states are new or old, because the rule will just grab whatever’s currently stored.

I know that it is possible using a rule, but I would like to do it on the Thing level, if possible.

Not possible.

You could do it with a JS Script Transformation but you’d be saving nothing doing it that way over a Rule. It’ll take the same amount of resources and the same number of lines of code.

1 Like

How would it be possible with a JS Transformation?
As far as I know, you only get the one input value into a Transformation and that’s it.

with JS Scripting you have access to all that the helper library offers. So you can ignore the input and just build your JSON using the states from the Items, same as you would from a rule.

Of course you’d have to apply the same transform to each channel. And if you had to use the input (maybe you can’t rely on the Item state yet where the transform is applied?), you’d have to apply a different transform for each channel.

I never did understand people’s hesitation to use rules. It’s the best solution for this use case. One rule with the publishMqtt action and you are done and all your processing is centralized and manageable.

2 Likes

Thanks, I was unaware that Transformations have access to all the helper libraries too.

As for why I’m choosing a transformation over a rule:
I like to have a separation of concerns here. If I implement this logic in a Transformation, I can just expose all the Channels independently and make use of everything else, like UOM and so on.
The rule would then contain the logic to get the weather from another item, and send commands to the NSPanel(MQTT) items.

If one would argue to go the extreme route and implement everything in rules, one could also argue that one “Socket” binding would be enough where you just send and retrieve bytes from a socket and implement everything else in rules. While this may be possible, it is still easier to “talk” to things and items which expose different states and commands and handle the translations themself.

I also want to have a generic way for NSPanels which I can then share back to the community and I for myself find it easier to implement something, I got from the community, if I don’t have to rely on rules someone else thought about, but can implement my own and just need to copy the things and items from someone else.

This is definitely true for JS Scripting and I’m pretty sure it’s true for jRuby and Rules DSL. I don’t know about the others through.

I am not suggesting that everything be implemented in rules. I am saying that for this specific case, using a transform is going to require at least as much code and resources and spread the implementation across multiple locations.

In this specific case, I don’t understand this statement at all. It’s basically the same code whether it’s in a transform or a rule. Putting it in a transformation doesn’t really impact anything when it comes to usability and understandability. It’s still the same code, just put in another location (multiple locations really) with different restrictions.

In fact, were it implemented in a rule, it could be converted to a rule template which makes it much easier for users to adopt and use. They can just install the template like an add-on and instantiate and configure a rule based on it without messing with copy/paste/edit. That’s not possible with transformations.

Obviously do what makes sense for you, but from my perspective you are jumping through hoops and imposing extra work on yourself (and others if you were to share this) just to avoid using a rule to do a task which rules are intended to do.

1 Like