Many Tasmota Devices OR how to update many UI-Switches

Hi together,
due to the fact, that my first question here got a so satisfying answer, I will try to explain my new Problem.

I have many Tasmota Devices (Switched-Power Outlets) and I switch them with UI Switches.
To have the UI-Switch displaying the correct state I subscribe to the corresponding MQTT Topic
stat/$DEVICE/POWER which is Updated on every switching cycle.
A second topic tele/$DEVICE/STATE is published in regularly intervals, which I also want to subscribe to in case NodeRed was started, it can also get the actual state.

My Problem now: I have 30+ Devices and my Idea was to subscribe to all Devices at once:
stat/+/POWER and tele/+/STATE which works great.
I have a function node parsing the Payload and storing the state to a global context.
A am also able to know which individual states changed since the last message (There are Tasmota Devices which have two or more outputs).
But how do I route the state to the corresponding UI-Switch Nodes?
My (not so elegant) approach is to add many Outputs to the function nodes and return the data like this:

var myMsg = {};
var v;
var myTopic = {};
var myState = {};

// Topic from tele/[$TOPIC]/STATE
myTopic = msg.topic.split("/")[1];
myMsg.topic = myTopic;
myState = global.get("tasmota_state") || {}; // retreive stored payload

// Decode Payload
// either from msg.payload.POWER or msg.payload.POWER1
// and if there are more POWER, change also the topic to $topic + "#"
if (msg.payload.hasOwnProperty("POWER")) {
    // POWER
    myMsg.payload = msg.payload.POWER;    
} else if (msg.payload.hasOwnProperty("POWER1")){
    // POWER1
    myMsg.payload = msg.payload.POWER1;    
}

// Store all Status Data in one Object "tasmota_state"
myState[myTopic] = myMsg.payload;
global.set("tasmota_state", myState); // store payload

// Return to according exit
if (myTopic === "hifi") {
    return [myMsg,v,v,v,v,v,v,v,v,v]
}
if (myTopic === "balkon") {
    return [v,myMsg,v,v,v,v,v,v,v,v]
}
if (myTopic === "bedlicht") {
    return [v,v,myMsg,v,v,v,v,v,v,v]
}
if (myTopic === "voegel") {
    return [v,v,v,myMsg,v,v,v,v,v,v]
}
if (myTopic === "bezzera") {
    return [v,v,v,v,myMsg,v,v,v,v,v]
}

It works for me. But what is the elegant way?

source.json (3.7 KB)

First, I suggest making stat/$DEVICE/POWER persistent, then you won't need to subscribe to the tele state topic as well.
Then I would adopt the simple solution of having an MQTT node next to each Switch node and feed them straight in. By using one MQTT node subscribed to them all you are just making life complicated.

1 Like

There isn't too many ways to make such routing. One is the way you are doing it now.
To make your function node more easy to read you can send out just one message with correct topic and wire it up to all switches but having filter before each ui_switch which then passes message only if the topic is correct for that switch

image

Colin's advise is also valid but that is if you don't care the states of your devices inside node-red. If you make some decisions based on diferent states of devices, then it is absolutely correct to store them as you do.

having an MQTT node next to each Switch node
I thought that this is from the performance side not elegant, as the MQTT-Broker has to handle a huge list of topics, where i am subscribed to. BTW: Does anyone know if NodeRed opens a new connection to the MQTT-Broker for each node, or are all subscriptions processed in one TCP-Session.

One of the main reasons I'm reworking everything in NodeRed and tweaking the structure, which has grown to 24 flows over the last 5+ years, is that the UI took about 30 seconds to load on my iPhone.

I think the biggest factor was that i had some charts in the UI that probably all needed to be loaded.

But while I'm at it, I now have the ambition to make my configuration beautiful.

Yes, that is a good idea, that will then probably be the solution how I will implement it

THANK YOU

Charts are best tools to help us make educated decisions to make any next move smarter. And that will be quite of step backwards if you have to get rid of them.

Yes, the dashboard can't handle charts too well. Of course the data optimization can help quite of bit but even then, the you never get adequate performance out of dashboard.

I have 10 charts on my custom made dashboard and it loads on mobile with 0.6 seconds.

I do recommend to go with the DIY. As long you have and idea how it should look and what it should do, the community will help you get going and achieve your goal. It just takes you to get started. :slight_smile:

2 Likes

It will make no significant difference to the overheads. It is all handled by one connection (assuming all the nodes use the same config node). In fact overall it will be more efficient as you will avoid all that testing and routing.

I don't understand what you mean by that. All you have to do is to route the mqtt messages to wherever you need them. I don't store any real world state in global context, I use persistent topics to store them all, with the big advantage that on restart all internal states get automatically aligned to the outside world by the initial messages from MQTT.

I am not sure this will be helpful but there again, it also may.

I wrote a subflow to make it modular and show the status of such devices.

[{"id":"94cf2ea396e5b6f6","type":"subflow","name":"MQTT Button Toggle","info":"# 2023 02 08\nAdded status text to icon to \nshow status of device.\n\n# 2023 01 25\nControl MQTT devices (power points)\nwith this node.\n\n`Button` node MUST have `topic` set to include\nthe device's name.  eg: `A1/button`\nand the payload MUST be `X`\n\nSet the `Device Name` to (eg) `A1` to\ncontrol `A1` device.\n","category":"","in":[{"x":70,"y":160,"wires":[{"id":"7207fbe19cc07923"}]}],"out":[{"x":990,"y":200,"wires":[{"id":"66fb54e88f4acb8d","port":0}]},{"x":1190,"y":160,"wires":[{"id":"27d2dd9a875ab1a5","port":0}]}],"env":[{"name":"DN","type":"str","value":"","ui":{"label":{"en-US":"Device Name"},"type":"input","opts":{"types":["str"]}}}],"meta":{},"color":"#DDAA99","inputLabels":["MQTT (tele and LWT) and button input"],"outputLabels":["Back to `button` input","MQTT output"],"status":{"x":1070,"y":270,"wires":[{"id":"45a85f7621f5c28f","port":0}]}},{"id":"541720ad73579447","type":"switch","z":"94cf2ea396e5b6f6","name":"what message?","property":"topic","propertyType":"msg","rules":[{"t":"cont","v":"/stat/POWER","vt":"str"},{"t":"cont","v":"/tele/LWT","vt":"str"},{"t":"cont","v":"button","vt":"str"}],"checkall":"true","repair":false,"outputs":3,"x":195,"y":160,"wires":[["61843ca1684f9f9d"],["4788b5265af1d45a"],["5b8cd9fe254ea8d1"]],"l":false},{"id":"61843ca1684f9f9d","type":"delay","z":"94cf2ea396e5b6f6","name":"Delay","pauseType":"delay","timeout":"200","timeoutUnits":"milliseconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"allowrate":false,"outputs":1,"x":340,"y":80,"wires":[["bdb2c46daf62b36f"]]},{"id":"6d4704f6fc9b98be","type":"change","z":"94cf2ea396e5b6f6","name":"RESET","rules":[{"t":"set","p":"payload","pt":"msg","to":"RESET","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":350,"y":120,"wires":[["bdb2c46daf62b36f"]]},{"id":"4788b5265af1d45a","type":"switch","z":"94cf2ea396e5b6f6","name":"LWT","property":"payload","propertyType":"msg","rules":[{"t":"eq","v":"Online","vt":"str"},{"t":"eq","v":"Offline","vt":"str"}],"checkall":"true","repair":false,"outputs":2,"x":340,"y":160,"wires":[["6d4704f6fc9b98be","bc2aabd04561d534"],["6a82c12953657965"]]},{"id":"6a82c12953657965","type":"function","z":"94cf2ea396e5b6f6","name":"BAN","func":"msg = {icon: '<font color = \"red\"><i class=\"fa fa-ban fa-3x\"></i></font>'};\nreturn msg;","outputs":1,"noerr":0,"x":340,"y":200,"wires":[["8dc8a0dd2b669ae3","704ae850657c9724"]]},{"id":"bc2aabd04561d534","type":"change","z":"94cf2ea396e5b6f6","name":"Enable","rules":[{"t":"set","p":"enabled","pt":"msg","to":"true","tot":"bool"},{"t":"set","p":"ARLEC-1","pt":"flow","to":"ONLINE","tot":"str"},{"t":"delete","p":"payload","pt":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":540,"y":160,"wires":[["66fb54e88f4acb8d"]]},{"id":"bdb2c46daf62b36f","type":"function","z":"94cf2ea396e5b6f6","name":"Set state","func":"let device = msg.topic.split(\"/\");\n//node.warn(\"Device \" + device[0]);\n//node.warn(\"Payload \" + msg.payload);\n\nif (msg.payload == \"RESET\")\n{\n    let state = context.get(device[0]);\n    if (state === 0)\n    {\n        //\n        msg= {\"icon\": '<font color = \"red\"><i class=\"fa fa-toggle-on fa-rotate-270 fa-3x\"></i></font>',\"background\":\"black\"};\n        return msg;\n    }\n    //\n}\nif (msg.payload == \"ON\")\n{\n    msg = { \"icon\": '<font color = \"lime\"><i class=\"fa fa-toggle-on fa-rotate-90 fa-3x\"></i></font>', \"background\": \"black\" };\n    context.set(\"STATE\",1);\n    context.set(device[0],1);\n} else\n{\n    msg = { \"icon\": '<font color = \"red\"><i class=\"fa fa-toggle-on fa-rotate-270 fa-3x\"></i></font>', \"background\": \"black\" };\n    context.set(\"STATE\",0);\n    context.set(device[0], 1);\n}\nreturn msg;\n","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":540,"y":120,"wires":[["66fb54e88f4acb8d"]]},{"id":"8dc8a0dd2b669ae3","type":"change","z":"94cf2ea396e5b6f6","name":"Disable","rules":[{"t":"set","p":"enabled","pt":"msg","to":"false","tot":"bool"},{"t":"set","p":"ARLEC-1","pt":"flow","to":"OFFLINE","tot":"str"},{"t":"delete","p":"payload","pt":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":540,"y":200,"wires":[["66fb54e88f4acb8d"]]},{"id":"5b8cd9fe254ea8d1","type":"function","z":"94cf2ea396e5b6f6","name":"Button control","func":"let msg1 = {};\n\nlet x = msg.payload;\nlet counter = context.get(\"counter\") || 0;\nif (x == \"X\")\n{\n    counter = (counter + 1) % 2;\n    context.set(\"counter\",counter);\n    if (counter === 0)\n    {\n        //\n        msg.payload = \"OFF\";\n        msg1 = {\"icon\": '<font color = \"orangered\"><i class=\"fa fa-toggle-on fa-rotate-270 fa-3x\"></i></font>',\"background\":\"black\"};\n\n    } else\n    {\n        //\n        msg.payload = \"ON\";\n        msg1 = {\"icon\": '<font color = \"orangered\"><i class=\"fa fa-toggle-on fa-rotate-90 fa-3x\"></i></font>',\"backgound\":\"black\"};\n    }\n}\nif (x == \"ON\")\n{\n    //  Force power ON\n    msg.payload = \"ON\";\n    msg1 = { \"icon\": '<font color = \"orangered\"><i class=\"fa fa-toggle-on fa-rotate-90 fa-3x\"></i></font>', \"backgound\": \"black\" };\n    context.set(\"counter\",1);\n}\nif (x == \"OFF\")\n{\n    //  Force power OFF\n    msg.payload = \"OFF\";\n    msg1 = { \"icon\": '<font color = \"orangered\"><i class=\"fa fa-toggle-on fa-rotate-90 fa-3x\"></i></font>', \"backgound\": \"black\" };\n    context.set(\"counter\",0);\n}\n\nreturn [msg,msg1];","outputs":2,"noerr":0,"initialize":"","finalize":"","libs":[],"x":750,"y":120,"wires":[["4f29a481b6f0d0e3","0ae8f43ccf9a1016"],["66fb54e88f4acb8d"]]},{"id":"4f29a481b6f0d0e3","type":"delay","z":"94cf2ea396e5b6f6","name":"Delay","pauseType":"delay","timeout":"100","timeoutUnits":"milliseconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"outputs":1,"x":1040,"y":120,"wires":[["27d2dd9a875ab1a5"]]},{"id":"27d2dd9a875ab1a5","type":"change","z":"94cf2ea396e5b6f6","name":"#1","rules":[{"t":"set","p":"topic","pt":"msg","to":"DN","tot":"env"},{"t":"set","p":"topic","pt":"msg","to":"topic&'/cmnd/power1'","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":1040,"y":160,"wires":[[]]},{"id":"7207fbe19cc07923","type":"switch","z":"94cf2ea396e5b6f6","name":"Only for this device","property":"topic","propertyType":"msg","rules":[{"t":"cont","v":"DN","vt":"env"},{"t":"eq","v":"MASTER","vt":"str"}],"checkall":"true","repair":false,"outputs":2,"x":135,"y":160,"wires":[["541720ad73579447"],["6a82c12953657965"]],"l":false},{"id":"0ae8f43ccf9a1016","type":"function","z":"94cf2ea396e5b6f6","name":"Identify","func":"let dev = env.get(\"DN\");\nlet status = msg.payload;\n\nnode.status(dev + \" \" + status);","outputs":1,"noerr":0,"initialize":"// Code added here will be run once\n// whenever the node is started.\nnode.status(env.get(\"DN\"));\n","finalize":"","libs":[],"x":540,"y":270,"wires":[[]]},{"id":"45a85f7621f5c28f","type":"status","z":"94cf2ea396e5b6f6","name":"","scope":["0ae8f43ccf9a1016","f9f07adecaa02241"],"x":770,"y":270,"wires":[[]]},{"id":"704ae850657c9724","type":"change","z":"94cf2ea396e5b6f6","name":"Wipe status when unplugged","rules":[{"t":"set","p":"payload","pt":"msg","to":" ","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":305,"y":270,"wires":[["0ae8f43ccf9a1016"]],"l":false},{"id":"66fb54e88f4acb8d","type":"junction","z":"94cf2ea396e5b6f6","x":680,"y":200,"wires":[[]]},{"id":"86852c080f02187b","type":"mqtt in","z":"6dd5ca4d.d1958c","g":"08979a5b752f5e66","name":"","topic":"A1/stat/POWER","qos":"2","datatype":"auto-detect","broker":"bbf26a6c.b7922","nl":false,"rap":true,"rh":0,"inputs":0,"x":3990,"y":3120,"wires":[["f29f3ae3ad90c065"]]},{"id":"1e39d8c837d7b2fb","type":"mqtt in","z":"6dd5ca4d.d1958c","g":"08979a5b752f5e66","name":"","topic":"A1/tele/LWT","qos":"2","datatype":"auto-detect","broker":"bbf26a6c.b7922","nl":false,"rap":true,"rh":0,"inputs":0,"x":4000,"y":3170,"wires":[["f29f3ae3ad90c065"]]},{"id":"61ee633777cbbe4d","type":"link in","z":"6dd5ca4d.d1958c","g":"08979a5b752f5e66","name":"Button","links":["c2f5a62f6628cf5f","9b45b54a54fd7017"],"x":4055,"y":3210,"wires":[["f29f3ae3ad90c065"]]},{"id":"f29f3ae3ad90c065","type":"subflow:94cf2ea396e5b6f6","z":"6dd5ca4d.d1958c","g":"08979a5b752f5e66","name":"","env":[{"name":"DN","value":"A1","type":"str"},{"name":"Device name","value":"A1","type":"str"}],"x":4200,"y":3170,"wires":[["9ba2764071e69ef2"],["a468b0cde187f892","4acfea6a575732bb"]]},{"id":"9ba2764071e69ef2","type":"ui_button","z":"6dd5ca4d.d1958c","g":"08979a5b752f5e66","name":"A1","group":"5dde4bf66eb38aa1","order":1,"width":"1","height":"1","passthru":false,"label":"{{msg.icon}}","tooltip":"","color":"","bgcolor":"{{msg.background}}","className":"","icon":"","payload":"X","payloadType":"str","topic":"A1/button","topicType":"str","x":4410,"y":3140,"wires":[["9b45b54a54fd7017"]]},{"id":"a468b0cde187f892","type":"mqtt out","z":"6dd5ca4d.d1958c","g":"08979a5b752f5e66","name":"","topic":"","qos":"","retain":"","respTopic":"","contentType":"","userProps":"","correl":"","expiry":"","broker":"bbf26a6c.b7922","x":4410,"y":3180,"wires":[]},{"id":"9b45b54a54fd7017","type":"link out","z":"6dd5ca4d.d1958c","g":"08979a5b752f5e66","name":"link out 3","mode":"link","links":["61ee633777cbbe4d"],"x":4505,"y":3140,"wires":[]},{"id":"bbf26a6c.b7922","type":"mqtt-broker","name":"TIMEPI MQTT","broker":"192.168.17.39","port":"1883","clientid":"","autoConnect":true,"usetls":false,"compatmode":false,"protocolVersion":"4","keepalive":"60","cleansession":true,"birthTopic":"SOM","birthQos":"0","birthRetain":"false","birthPayload":"BedPi Comms UP","birthMsg":{},"closeTopic":"EOM","closeQos":"0","closeRetain":"false","closePayload":"BedPi Shutting DOWN","closeMsg":{},"willTopic":"EOM","willQos":"0","willRetain":"false","willPayload":"BedPi COMMS FAILURE","willMsg":{},"userProps":"","sessionExpiry":""},{"id":"5dde4bf66eb38aa1","type":"ui_group","name":"A(1-4) Sockets","tab":"e92ac76d.c24e7","order":13,"disp":true,"width":"4","collapse":false,"className":""},{"id":"e92ac76d.c24e7","type":"ui_tab","name":"Real_World_Control","icon":"dashboard","order":5,"disabled":false,"hidden":false}]

This is an entire flow for this subflow to show you how to use it.
The subflow is the item of interest.

Yes, it probably does have some short comings mentioned above.
But it may be of some use.

This topic was automatically closed 60 days after the last reply. New replies are no longer allowed.