three state switch

3,582 views
Skip to first unread message

Cor Bosman

unread,
Feb 5, 2017, 5:03:55 PM2/5/17
to Node-RED
Hi all, im working on automating a terrarium. It's mostly run on different timers inside node-red, but id like to have manual overrides for each device. Im having a bit of trouble with the ui design inside the dashboard.

Ideally what i would like is to be able to have a user pick between 3 options:  ON/OFF/AUTO.  ON/OFF would override the timers, and AUTO would revert back to whatever the timers are saying.  Imagine someone visiting, and I want to show the fogger system. I would switch the toggle to ON, or even tell alexa to turn it on. Then, when the show is over, Id put it back on auto.  

I could of course use a dropdown for this. But it's not the most visually appealing.  A styled 3-state radio button would be nice, but no radio buttons in the dashboard. 

So unless I use the dropdown, I guess my only option is to use a template node, and pull in my own template?  Although im not quite sure how that would work without creating a new template for each device. 

Any other options Im overlooking?  

Thanks

Colin Law

unread,
Feb 5, 2017, 5:27:20 PM2/5/17
to node...@googlegroups.com
You could have two switches, On/Off and Auto/Manual. When in Manual
the On/Off switch control the state, When Auto is selected then the
On/Off switch themselves to show the current state. If On/Off is
changed manually while in Auto then Auto automatically switches to
manual.

Colin
> --
> http://nodered.org
>
> Join us on Slack to continue the conversation: http://nodered.org/slack
> ---
> You received this message because you are subscribed to the Google Groups
> "Node-RED" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to node-red+u...@googlegroups.com.
> To post to this group, send email to node...@googlegroups.com.
> Visit this group at https://groups.google.com/group/node-red.
> To view this discussion on the web, visit
> https://groups.google.com/d/msgid/node-red/8841a1fe-3e51-4608-9ff4-f02ff280b3c3%40googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.

Cor Bosman

unread,
Feb 5, 2017, 5:49:20 PM2/5/17
to Node-RED
That is certainly possible and an option I looked at, but it's a little hairy.  What are you going to do when you select MANUAL.  Immediately set the state of the  ON/OFF?  That might not be what you want. I guess you could try to always keep the state of the ON/OFF in sync with the state of the timers. But it gets complex pretty quick.  

Actually, im kind of ok where im at now. I styled the dropdown to the point where you can barely tell it's a dropdown.  But if anyone has any other ideas?




 





Colin Law

unread,
Feb 6, 2017, 3:42:44 AM2/6/17
to node...@googlegroups.com
On 5 February 2017 at 22:49, Cor Bosman <cor.b...@gmail.com> wrote:
That is certainly possible and an option I looked at, but it's a little hairy.  What are you going to do when you select MANUAL.  Immediately set the state of the  ON/OFF?  That might not be what you want. I guess you could try to always keep the state of the ON/OFF in sync with the state of the timers. But it gets complex pretty quick.  

Not all that complex. First, if you are not already doing so, use MQTT to pass the values between the UI and control system (this is a good idea anyway so you can simultaneously view the UI on multiple devices).  So the switches' outputs go to MQTT topics myhouse/terrarium/auto and myhouse/terrarium/on. Also in the UI feed the current values of those topics *into* the switches (but make sure you uncheck message pass through in the switches so you don't get a loop). Send the output of the timers to the on/off topic also.  The control system flow picks up those values in order to know what to do. The result of the above is that the switches automatically show the current state of auto/manual and on/off. Then all you need is a little logic that detects on/off changing in the ui and changes the auto/manual state accordingly.

Colin

Cor Bosman

unread,
Feb 6, 2017, 7:34:55 AM2/6/17
to Node-RED
Hi Colin, thanks for your tips. Although I actually like my current dropdown, your post have given me some tips on how to handle other moving parts. I totally overlooked the not passing of messages through switches. That makes life so much easier.  I use MQTT heavily.  My relays are all behind mqtt funnels so I can feed them from multiple sources (alexa, switches, timers, etc). But I wasnt feeding the state back through mqtt, thats a good idea wrt multiple clients using it and with not passing messages through then it becomes easy. 

I may try out 2 switches anyways, just to see what it looks like. Do you know if you can change the colors of the on/off state of switches? I know you can load your own icons, but it just doesnt look as nice as the default switch. I just want  to make the off state more visual using red. 

Cor Bosman

unread,
Feb 6, 2017, 11:54:49 AM2/6/17
to Node-RED
Hi Colin, sorry to revive this, but maybe you can offer some advice here.  I'll try to contain the issue to a small section of my flows. You mentioned I could send the state of the dropdown into the dropdown, and then dont pass it on. That would work, but there is a complication. Check the flow below. It is a simplification, in reality I have some extra function nodes that do some message parsing. 

Im using BigTimer to control my devices on time schedules. But, I have dropdowns to override them, which you do by sending the override command to the input of BigTimer. So, when I need to re-load state on startup, I not only have to set the state of the switches, but also the state of the timers so they know if they are being overridden or not.  This is why I let the messages pass through the switch. Basically when I re-load state, I simply set the dropdown through their input, and let the message pass through to the timers. 

Are you saying you would just set the initial state of the timers and dropdowns separately?  Even though the dropdowns are connected anyways? 

Right now, when I need to manually control a device through other means than the UI element, for instance through Alexa, I just send an mqtt message to the input of the switch.  Not good?

[{"id":"4476fac3.9560e4","type":"bigtimer","z":"624328bc.995828","outtopic":"moonlight","outpayload1":"on","outpayload2":"off","name":"Moonlight","lat":"52","lon":"4","starttime":"1275","endtime":"480","startoff":0,"endoff":0,"offs":0,"outtext1":"","outtext2":"","timeout":"1440","sun":true,"mon":true,"tue":true,"wed":true,"thu":true,"fri":true,"sat":true,"jan":true,"feb":true,"mar":true,"apr":true,"may":true,"jun":true,"jul":true,"aug":true,"sep":true,"oct":true,"nov":true,"dec":true,"day1":0,"month1":0,"day2":0,"month2":0,"day3":0,"month3":0,"day4":0,"month4":0,"day5":0,"month5":0,"d1":0,"w1":0,"d2":0,"w2":0,"d3":0,"w3":0,"d4":0,"w4":0,"d5":0,"w5":0,"suspend":false,"random":false,"repeat":true,"atstart":true,"x":667,"y":1158,"wires":[["a2d5c38f.81c4c"],[],[]]},{"id":"e01917.333666e8","type":"ui_dropdown","z":"624328bc.995828","name":"Moon Light Dropdown","label":"","group":"27dfc367.cf784c","order":2,"width":"3","height":"1","passthru":true,"options":[{"label":"ON","value":"on","type":"str"},{"label":"AUTO","value":"auto","type":"str"},{"label":"OFF","value":"off","type":"str"}],"payload":"","topic":"moonlight","x":459,"y":1158,"wires":[["4476fac3.9560e4"]]},{"id":"69dc2252.22840c","type":"mqtt in","z":"624328bc.995828","name":"switch ","topic":"","qos":"2","broker":"baacc82a.613218","x":244,"y":1159,"wires":[["e01917.333666e8"]]},{"id":"a2d5c38f.81c4c","type":"mqtt out","z":"624328bc.995828","name":"control device","topic":"output","qos":"","retain":"","broker":"baacc82a.613218","x":860,"y":1146,"wires":[]},{"id":"27dfc367.cf784c","type":"ui_group","z":"","name":"Buttons","tab":"a92c5d74.e98a1","order":4,"disp":false,"width":"6"},{"id":"baacc82a.613218","type":"mqtt-broker","z":"","broker":"127.0.0.1","port":"1883","clientid":"","usetls":false,"compatmode":true,"keepalive":"60","cleansession":true,"willTopic":"","willQos":"0","willPayload":"","birthTopic":"","birthQos":"0","birthPayload":""},{"id":"a92c5d74.e98a1","type":"ui_tab","z":"","name":"Home","icon":"dashboard","order":2}]

 


Colin Law

unread,
Feb 6, 2017, 12:23:01 PM2/6/17
to node...@googlegroups.com
Is suggest keeping all the UI stuff (switches, dropdowns etc) in
separate flows, probably one for each tab on the UI. That makes it
easier to maintain. Do all comms with the rest of the system through
mqtt. So for your situation have a tab with dropdown, fed from mqtt
and outputting to mqtt. Then on a separate tab pick up that mqtt and
give it to the timer.

You say that when you want to control the device through other means
you send it to the switch, in fact you should think of this as sending
it to mqtt, (which is exactly what your switch does), then the rest of
the system picks it up from mqtt and drives the device.

Similarly I suggest keeping all the actual device I/O on separate
tab(s) too, again communicating via mqtt so that the logic of the
control system (in this simplified case the timer) is separated by
mqtt from the logic driving the gpio pins or whatever the device is.
Using that sort of technique will help to prevent the flows from
getting overcomplex. You end up with a larger number of flows, each
containing simpler logic.

Colin
> --
> http://nodered.org
>
> Join us on Slack to continue the conversation: http://nodered.org/slack
> ---
> You received this message because you are subscribed to the Google Groups
> "Node-RED" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to node-red+u...@googlegroups.com.
> To post to this group, send email to node...@googlegroups.com.
> Visit this group at https://groups.google.com/group/node-red.
> To view this discussion on the web, visit
> https://groups.google.com/d/msgid/node-red/632bbf98-343a-4a98-a7ba-4dfc65fcfae2%40googlegroups.com.

Cor Bosman

unread,
Feb 6, 2017, 12:52:25 PM2/6/17
to Node-RED
It's pretty much all on their own tabs except for the dropdowns and timers which I combined into one tab because i connected them.  Everything else communicates through mqtt.  Didnt think about splitting the dropdowns and timers and using mqtt between them. So obvious though, duh.  Thanks.

Colin Law

unread,
Feb 6, 2017, 4:08:18 PM2/6/17
to node...@googlegroups.com
Glad to be of help

Cheers

Colin
> --
> http://nodered.org
>
> Join us on Slack to continue the conversation: http://nodered.org/slack
> ---
> You received this message because you are subscribed to the Google Groups
> "Node-RED" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to node-red+u...@googlegroups.com.
> To post to this group, send email to node...@googlegroups.com.
> Visit this group at https://groups.google.com/group/node-red.
> To view this discussion on the web, visit
> https://groups.google.com/d/msgid/node-red/e6943d9c-0a84-45ec-b440-79d405e4601c%40googlegroups.com.

Cor Bosman

unread,
Feb 7, 2017, 3:25:29 PM2/7/17
to Node-RED
Much cleaner now, thanks again.

One more question. What is best practises regarding mqtt and many states. Lets say I want to control the state of 20 dropdowns using mqtt input. Do you create 20 mqtt nodes and feed them into each dropdown, or do people generally have 1 mqtt node, and feed that into a switch node that branches out into dropdowns? 

Dave C-J

unread,
Feb 7, 2017, 3:45:02 PM2/7/17
to node...@googlegroups.com
up to you... the mqtt node(s) will reuse the same connection (config node), so as efficient either way. Probably multiple mqtt nodes will be "easier" to read in the flow.

Julian Knight

unread,
Feb 8, 2017, 8:23:04 AM2/8/17
to Node-RED
Another thought.

What about having 3 toggle buttons on top of each other and use Angluar to only ever allow 1 button to be on? I think that's how I would do it. 

Cor Bosman

unread,
Feb 8, 2017, 8:56:05 AM2/8/17
to Node-RED
Hi Julian, with toggle buttons do you mean 3 button nodes? Something like this first image would be nice, but I dont see how one can programmatically alter the background color of button nodes. Or did you mean to use template nodes? That would mean about 12 x 3 or 36 individually tailored templates?  It also needs to all fit on a tablet screen.  And on top of that, I also have to show the actual current state in case the device is set to auto.  Right now I have what you see in the second image. 

Julian Knight

unread,
Feb 9, 2017, 8:31:42 AM2/9/17
to Node-RED
Sorry, what I meant was to still use the Dashboard template node - use that to create 3 Anglular Material buttons - you can watch each in the code and whenever one changes to on, turn off the other 2. Programmatically change the colours using ng-style which is data driven.

To get round the problem of having 12 different templates (not 36 since a single template will contain all 3 buttons). Hard to advise the best approach with minimal information but if you want all 12 combinations on the same page, I'd probably start by trying to get the input data all into a single msg so you can use Anglular's repeat function around the 3 buttons, creating all the buttons in groups of 3 in one go. This would let you have any number of entries with the only real limitation being how much you want users to have to scroll. All from a single template node.

For the current state for auto - you could build that into the colour scheme of the buttons if you wanted to, alternatively, in addition to the 3 buttons - add an indicator "light" after the buttons, this would be within your ng-repeat loop and fed from the aggregate data. So your input msg might look something like:

{'topic':'deviceStatus', payload: [
   
{ 'Name': 'Light 1',  'SwitchState':'Auto', 'CurrentStatus': 'On' }
   
{ 'Name': 'Mister 1',  'SwitchState':'On', 'CurrentStatus': 'On' }

   
...
] }


I realise this makes the template node quite complex - compared to using the dedicated switch nodes, but this is typical of front-end programming. And you will be making good use of Anglular's strengths. It does mean you learning some more about Angular though I'm afraid.

Cor Bosman

unread,
Feb 9, 2017, 10:37:54 AM2/9/17
to Node-RED
Ok, i'll play with this.  I have plenty of experience with VueJS so I can hopefully figure it out.

Right now I basically treat the LEDs as physical outputs. So they get triggered by the output of the BigTimer nodes and reflect the actual state of the physical devices instead of a state in memory.


Julian Knight

unread,
Feb 10, 2017, 5:08:37 AM2/10/17
to Node-RED
OK, I think you can just extend that model a little to incorporate the buttons and their state. The main difference being that you need to merge the data into a single object covering all of the outputs and pass that into a single template.

I've not had the chance to really play with Vue yet, though I want to, it appears to me to be vastly simpler to use than Angular. I did start to put together a design for an alternative to the NR Dashboard using Vue. Something that would sit, in complexity terms, between the HTTP output and the Dashboard and would create a websocket for each node as the dashboard does but wouldn't come with the necessary bagage that the dashboard has to have to cater for relatively non-technical users. Sadly I've had no time to work on it recently due to pressure of work.

Cor Bosman

unread,
Feb 10, 2017, 6:50:01 PM2/10/17
to Node-RED
I played around with Angular and it's pretty easy to grasp.  Right now I ended up with using 1 template for each row, and a separate LED template. I could encode the LED  in the auto button (just turn the button red or green) but Im leaning towards this now because I find it easier to spot from a distance if I have anything on non-auto.  If I see any non-yellow buttons I have manual overrides.  This is all running great now, it reacts as it should to input changes on the input side, and send out input changes to the output side.



I tried getting it all in 1 template, and showing X rows of 3 buttons and an LED was pretty straightforward with ng-repeat but I was running into trouble of how to actually manage state changes from the input side of the template. I wasnt quite sure what I would use as a trigger to send a new msg to the template.  Usually I just get a signal from 1 button change.  This is broadcast on MQTT "terrarium/input/+".  And a physical change is broadcast on "terrarium/output/+".  I could try and grab a global.get('state') or whatever, but I cant be absolutely sure it has the state change itself because it also relies on that same MQTT channel. 

I suppose I could let the "statemachine flow" (which handles setting the global state from mqtt input) broadcast a message with the full state after each state change and use that as input for this template?

Do you have any more wisdom on this for me?  Anything obvious im just missing?

Cor Bosman

unread,
Feb 11, 2017, 9:39:16 AM2/11/17
to Node-RED
A new day and I decided to implement the idea I had while posting the previous post by having the state flow broadcast an MQTT full state message. That worked perfectly. I was able to put it all in one template now, so much cleaner.  

How it works. I have a flow which listens to mqtt messages on "terrarium/input/+" and stores any changes in a global state object. It then broadcasts the full object to "terrarium/state". The buttons flow gets that as input. The template loops over all devices in the state object, and builds the matrix of buttons. Then when you click a button, a message is sent to "terrarium/input/+" which then loops back to the global state flow, and from there to the input of the template. It's so fast you dont even notice that the click doesnt immediately set the state.


[{"id":"f1544881.21efa8","type":"mqtt out","z":"20bc8d1a.8acb72","name":"terrarium/input/+","topic":"","qos":"","retain":"","broker":"1c2a9091.af757f","x":720.5,"y":749,"wires":[]},{"id":"e33dcbdc.df2818","type":"ui_template","z":"20bc8d1a.8acb72","group":"9bff997e.a98658","name":"buttons","order":1,"width":"9","height":"10","format":"\n<div ng-repeat=\"(key, device) in msg.payload\">\n\n    <div class=\"btn-row\" layout=\"row\" layout-sm=\"column\" layout-align=\"center center\" layout-wrap>\n        <span class=\"btn-name\" flex=\"30\">{{ device.name }}</span>\n            \n        <md-button \n            ng-click=\"send({payload: 'on', topic: 'terrarium/input/'+key});\"\n            ng-class=\"device.state.input === 'on' ? 'btn-selected' : ''\" \n            class=\"md-raised btn btn-on\" \n            flex>\n            ON\n        </md-button>\n            \n        <md-button \n            ng-click=\"send({payload: 'auto', topic: 'terrarium/input/'+key});\"\n            ng-class=\"device.state.input === 'auto' ? 'btn-selected' : ''\" \n            class=\"md-raised btn btn-auto\" \n            flex>\n            AUTO\n        </md-button>\n            \n        <md-button \n            ng-click=\"send({payload: 'off', topic: 'terrarium/input/'+key});\"\n            ng-class=\"device.state.input === 'off' ? 'btn-selected' : ''\" \n            class=\"md-raised btn btn-off\" \n            flex>\n            OFF\n        </md-button>\n        \n        <md-led \n            ng-class=\"device.state.output === 'on' ? 'md-led-on' : 'md-led-off'\"\n            class=\"md-led\"\n        </md-led>\n            \n    </div>\n</div>\n\n\n<style>\n    :focus {\n        outline: 0;\n    }\n\n    .btn-row {\n        margin-bottom: 10px;\n    }\n    \n    .nr-dashboard-theme-light .md-button.btn {\n        color: #000;\n        padding: 15px;\n        margin: 0px 10px 0px 0px;\n        background-color: #c3c3c3;\n        border: 1px solid #ababab;\n        box-shadow: 4px 4px 6px 0 #dadada;\n    }\n    \n    .nr-dashboard-theme-light .md-button.btn.btn-off.btn-selected {\n        background-color: #F4511E;\n    }\n    \n    .nr-dashboard-theme-light .md-button.btn.btn-on.btn-selected {\n        background-color: #00FF00;\n    }\n    \n    .nr-dashboard-theme-light .md-button.btn.btn-auto.btn-selected {\n        background-color: #f4c61e;\n    }\n    \n    \n    .btn-name {\n        text-align: center;\n        font-weight: bold;\n    }\n    \n\n    .md-led {\n        margin-left: 15px;\n        border-radius: 50%;\n\t    width: 20px;\n\t    height: 20px; \n\t    border: 1px solid #666666;\n    }\n    \n    .md-led.md-led-on {\n        background: #00ff00; /* Old browsers */\n        background: -moz-radial-gradient(center, ellipse cover, #00ff00 0%, #00c406 100%); /* FF3.6-15 */\n        background: -webkit-radial-gradient(center, ellipse cover, #00ff00 0%,#00c406 100%); /* Chrome10-25,Safari5.1-6 */\n        background: radial-gradient(ellipse at center, #00ff00 0%,#00c406 100%); /* W3C, IE10+, FF16+, Chrome26+, Opera12+, Safari7+ */\n        filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#00ff00', endColorstr='#00c406',GradientType=1 ); /* IE6-9 fallback on horizontal gradient */\n    }\n    \n    .md-led.md-led-off {\n        background: #ff0000; /* Old browsers */\n        background: -moz-radial-gradient(center, ellipse cover, #ff0000 0%, #d61800 95%, #b70300 100%); /* FF3.6-15 */\n        background: -webkit-radial-gradient(center, ellipse cover, #ff0000 0%,#d61800 95%,#b70300 100%); /* Chrome10-25,Safari5.1-6 */\n        background: radial-gradient(ellipse at center, #ff0000 0%,#d61800 95%,#b70300 100%); /* W3C, IE10+, FF16+, Chrome26+, Opera12+, Safari7+ */\n        filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ff0000', endColorstr='#b70300',GradientType=1 ); /* IE6-9 fallback on horizontal gradient */\n    }\n \n</style>\n","storeOutMessages":false,"fwdInMessages":false,"x":496,"y":748,"wires":[["f1544881.21efa8"]]},{"id":"9c2ac1a3.43c82","type":"mqtt in","z":"20bc8d1a.8acb72","name":"","topic":"terrarium/state","qos":"2","broker":"60f8fb33.312b64","x":129.5,"y":747,"wires":[["50129668.ef8148"]]},{"id":"50129668.ef8148","type":"json","z":"20bc8d1a.8acb72","name":"","x":321.5,"y":747,"wires":[["e33dcbdc.df2818"]]},{"id":"b701e15c.a2ee4","type":"comment","z":"20bc8d1a.8acb72","name":"buttons","info":"","x":97.5,"y":699,"wires":[]},{"id":"1c2a9091.af757f","type":"mqtt-broker","z":"","broker":"127.0.0.1","port":"1883","clientid":"nodered","usetls":false,"compatmode":true,"keepalive":"60","cleansession":true,"willTopic":"","willQos":"0","willPayload":"","birthTopic":"","birthQos":"0","birthPayload":""},{"id":"9bff997e.a98658","type":"ui_group","z":"","name":"Controls","tab":"7435c4fd.9bb8ac","disp":false,"width":"9"},{"id":"60f8fb33.312b64","type":"mqtt-broker","z":"","broker":"localhost","port":"1883","clientid":"","usetls":false,"compatmode":true,"keepalive":"15","cleansession":true,"willTopic":"","willQos":"0","willPayload":"","birthTopic":"","birthQos":"0","birthPayload":""},{"id":"7435c4fd.9bb8ac","type":"ui_tab","z":"","name":"Home","icon":"dashboard","order":2}]


Julian Knight

unread,
Feb 11, 2017, 11:30:56 AM2/11/17
to Node-RED
That's what I had in mind :)

Of course, it means that you are shifting more data around but the amount is pretty small anyway so shouldn't ever be an issue. Glad it is working for you and it looks good from your previous screenshot.

Might be good as a quick write-up in the flows site since I image that this would be something that others would want to use.

Cor Bosman

unread,
Feb 11, 2017, 1:22:02 PM2/11/17
to Node-RED
Since all the moving parts are sort of connected, I decided to just make a flow that explains my terrarium automation (minus a whole bunch of unrelated stuff), including how to make a button panel using a single dashboard template. Hope it's useful to people. This flow will immediately work after import so people can play with it.

Julian Knight

unread,
Feb 11, 2017, 5:22:38 PM2/11/17
to Node-RED
Good stuff, thanks for sharing.

TAllen-t

unread,
Nov 10, 2017, 2:00:44 AM11/10/17
to Node-RED
The buttons are not changing colors for me as they appear in your post above. Any idea what i might be doing wrong?

c...@in.ter.net

unread,
Nov 10, 2017, 6:39:46 AM11/10/17
to node...@googlegroups.com
That flow is 9 months old, and there have been a few node-red updates since then. Probably something changed that impacted those flows. I am currently using a slightly different system than that. I got rid of the little round icon at the end. It now looks like this:  http://shot.ter.net/img/2017/201711101218-f2b25b60bade08b6a783f71ade0fb9e4.png. It’s been running my terrarium flawlessly for all that time. 


I also stopped using BigTimer because it didnt really work well in my specific scenario. It’s a great timer, but it didnt really work well for me trying to turn things on/off multiple times a day. My flows got too complicated just to work around BigTimer.  So I wrote my own that allows multiple on/off a day and was a bit easier to configure for these relatively simple scenarios:  https://flows.nodered.org/node/node-red-contrib-timerswitch 

Cor


Reply all
Reply to author
Forward
0 new messages