Multi-State Toggle Examples using UI-Template and CCS Overrides

This is a set of Multi-State UI Toggles that are useful in Automation and Control Scenarios.

They are based off of the work of the Forum User HotNIPI. Here's a link to the NodeRED Forum where I initiated the request..and while still discussing it... other users jumped in to help.

https://discourse.nodered.org/t/feature-request-off-auto-on-toggle/40837

Perhaps someday an actual UI Node will be developed based on this work.(see update at bottom of this readme) For now it is easy to implement with a UI Template to perform a few Cascade Style Sheet Overrides for these new Toggle Classes and of course a UI template to define and implement each of the 3 new toggle classes.

This flow contains the raw work done by HotNIPI, with the exception that I have changed the formatting so that they render very nicely in a compact form on the Standard 7" 800x480 Official RaspberryPi TouchLCD Display.

The flow also contains a few examples that I've created for Automation Control Scenarios, like selecting Control Mode, Setpoint Mode, Device Mode... and simply turning something on/off.

I hope that you find them useful.

It has come to my attention that there is a node in development that does exactly what these UI-Template examples do.... It is written by forum member BartButenaers. It's still in development, and doesn't quite scale as well on the smaller 24x24 grid sizes and 4 x 4 widget spacing needed to get meaningful quantity of content on the Standard 7" 800x480 Official RaspberryPi TouchLCD Display. It will be interesting to see what the developer does going forward... already it is quite flexible and good! Here is the announcement in the Node-RED Forum for this 'under-development' node:

https://discourse.nodered.org/t/announce-node-red-contrib-ui-multistate-switch-beta/41047/12

[{"id":"ca5421c2.cc3a5","type":"tab","label":"UIToggles","disabled":false,"info":""},{"id":"438a95b9.6a9eac","type":"ui_template","z":"ca5421c2.cc3a5","group":"9f2da61.3353758","name":"Dashboard UIToggle CCS","order":2,"width":12,"height":1,"format":"<style id=\"dashboard-styles-override\">\n.switchwrapper {\n  margin: auto 0;\n}\n.toggle_radio{\n    margin: auto;\n    overflow: hidden;\n    border-radius: 50px;\n    position: relative;\n    height: 19px;\n    border: 1px solid var(--nr-dashboard-groupBorderColor);\n    background: var(--nr-dashboard-widgetBgndColor);\n}\n.toggle_radio > * {\n  float: left;\n}\n.toggle_radio input[type=radio]{\n  display: none;\n}\n.toggle_4{\n     width: 25%;\n}\n.toggle_3{\n     width: 33%;\n}\n.toggle_2{\n     width: 50%;\n}\n.toggle_radio label{\n  display: block;\n  height: 15px;\n  margin: 0;\n  line-height :15px;\n  border-radius: 50px;\n  cursor: pointer;\n  z-index: 1;\n  text-align: center;\n}\n.toggle_radio label >p {\n    background:transparent !important;\n}\n.toggle_option_slider{\n  \n  height: 15px;\n  position: absolute;\n  top: 2px;\n  border-radius: 50px;\n  transition: all .3s ease;\n  z-index:0;\n  left:2px;\n  opacity:0.5;\n  background: var(--nr-dashboard-widgetColor);\n}\n</style>","storeOutMessages":true,"fwdInMessages":true,"resendOnRefresh":true,"templateScope":"global","x":130,"y":40,"wires":[[]]},{"id":"c770e530.b74478","type":"ui_template","z":"ca5421c2.cc3a5","group":"e2c488d6.e06ad8","name":"3-way switch","order":2,"width":6,"height":1,"format":"<div id=\"{{'swc_'+$id}}\" class=\"switchwrapper\">\n  <div class=\"toggle_radio\">\n    <input type=\"radio\" class=\"toggle_option toggle_3\" id=\"{{'first_'+$id}}\" ng-model=\"value\" value=\"first\">\n    <input type=\"radio\" class=\"toggle_option toggle_3\" id=\"{{'second_'+$id}}\" ng-model=\"value\" value=\"second\">\n    <input type=\"radio\" class=\"toggle_option toggle_3\" id=\"{{'third_'+$id}}\" ng-model=\"value\" value=\"third\">\n    <div id=\"{{'slider_'+$id}}\" class=\"toggle_option_slider toggle_3\"></div>\n    <label class=\" toggle_3\" for=\"{{'first_'+$id}}\"><p>A</p></label>\n    <label class=\" toggle_3\" for=\"{{'second_'+$id}}\"><p>B</p></label>\n    <label class=\" toggle_3\" for=\"{{'third_'+$id}}\"><p>C</p></label>\n  </div>\n  \n</div>\n\n<script>\n(function(scope) {\n    scope.incomingChange = false\n    scope.$watch('value', function(value) {\n        switch(value){\n            case 'first':{\n                $(\"#slider_\"+scope.$id).css(\"left\", \"3px\")\n                break\n            }\n            case 'second':{\n                $(\"#slider_\"+scope.$id).css(\"left\", \"33%\")\n                break\n            }\n            case 'third':{\n                $(\"#slider_\"+scope.$id).css(\"left\", \"66%\")\n                break\n            }\n        }\n        if(scope.incomingChange == true){\n            scope.incomingChange = false\n            return\n        }\n        if(!value){\n            return\n        }\n        scope.send({payload: value})\n       \n    });\n    scope.$watch('msg', function(msg) {\n        if(msg){\n            if(scope.value != msg.payload){\n                scope.incomingChange = true\n                scope.value = msg.payload\n            }\n            \n        }\n    });\n})(scope);\n</script>\n  \n","storeOutMessages":true,"fwdInMessages":true,"resendOnRefresh":true,"templateScope":"local","x":350,"y":240,"wires":[["e5fbeffc.05654"]]},{"id":"e5fbeffc.05654","type":"debug","z":"ca5421c2.cc3a5","name":"Selected option","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":560,"y":240,"wires":[]},{"id":"fffca1ba.ef777","type":"inject","z":"ca5421c2.cc3a5","name":"Select first","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"first","payloadType":"str","x":120,"y":200,"wires":[["c770e530.b74478"]]},{"id":"5d4d028f.3736dc","type":"inject","z":"ca5421c2.cc3a5","name":"Select second","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"second","payloadType":"str","x":110,"y":240,"wires":[["c770e530.b74478"]]},{"id":"32a2f720.f367b8","type":"inject","z":"ca5421c2.cc3a5","name":"Select third","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"third","payloadType":"str","x":120,"y":280,"wires":[["c770e530.b74478"]]},{"id":"de21e3f1.82753","type":"ui_template","z":"ca5421c2.cc3a5","group":"e2c488d6.e06ad8","name":"2-way switch","order":1,"width":6,"height":1,"format":"<div id=\"{{'swc_'+$id}}\" class=\"switchwrapper\">\n  <div class=\"toggle_radio\">\n    <input type=\"radio\" class=\"toggle_option\" id=\"{{'first_'+$id}}\" ng-model=\"value\" value=\"first\">\n    <input type=\"radio\" class=\"toggle_option\" id=\"{{'second_'+$id}}\" ng-model=\"value\" value=\"second\">\n    \n    <div id=\"{{'slider_'+$id}}\" class=\"toggle_option_slider toggle_2\"></div>\n    <label class=\" toggle_2\" for=\"{{'first_'+$id}}\"><p>A</p></label>\n    <label class=\" toggle_2\" for=\"{{'second_'+$id}}\"><p>B</p></label>\n\n  </div>\n  \n</div>\n\n<script>\n(function(scope) {\n    scope.incomingChange = false\n    scope.$watch('value', function(value) {\n        switch(value){\n            case 'first':{\n                $(\"#slider_\"+scope.$id).css(\"left\", \"2px\")\n                break\n            }\n            case 'second':{\n                $(\"#slider_\"+scope.$id).css(\"left\", \"49%\")\n                break\n            }\n\n        }\n        if(scope.incomingChange == true){\n            scope.incomingChange = false\n            return\n        }\n        if(!value){\n            return\n        }\n        scope.send({payload: value})\n       \n    });\n    scope.$watch('msg', function(msg) {\n        if(msg){\n            if(scope.value != msg.payload){\n                scope.incomingChange = true\n                scope.value = msg.payload\n            }\n            \n        }\n    });\n})(scope);\n</script>\n  \n","storeOutMessages":true,"fwdInMessages":true,"resendOnRefresh":true,"templateScope":"local","x":350,"y":160,"wires":[["56b0f8.831e0f08"]]},{"id":"56b0f8.831e0f08","type":"debug","z":"ca5421c2.cc3a5","name":"Selected option","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":560,"y":160,"wires":[]},{"id":"96d99491.c677b8","type":"inject","z":"ca5421c2.cc3a5","name":"Select first","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"first","payloadType":"str","x":120,"y":120,"wires":[["de21e3f1.82753"]]},{"id":"a07e0e6e.4244c","type":"inject","z":"ca5421c2.cc3a5","name":"Select second","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"second","payloadType":"str","x":110,"y":160,"wires":[["de21e3f1.82753"]]},{"id":"5ec61ac2.9a01c4","type":"ui_template","z":"ca5421c2.cc3a5","group":"e2c488d6.e06ad8","name":"4-way switch","order":3,"width":6,"height":1,"format":"<div id=\"{{'swc_'+$id}}\" class=\"switchwrapper\">\n  <div class=\"toggle_radio\">\n    <input type=\"radio\" class=\"toggle_option\" id=\"{{'first_'+$id}}\" ng-model=\"value\" value=\"first\">\n    <input type=\"radio\" class=\"toggle_option\" id=\"{{'second_'+$id}}\" ng-model=\"value\" value=\"second\">\n    <input type=\"radio\" class=\"toggle_option\" id=\"{{'third_'+$id}}\" ng-model=\"value\" value=\"third\">\n    <input type=\"radio\" class=\"toggle_option\" id=\"{{'fourth_'+$id}}\" ng-model=\"value\" value=\"fourth\">\n    <div id=\"{{'slider_'+$id}}\" class=\"toggle_option_slider toggle_4\"></div>\n    <label class=\" toggle_4\" for=\"{{'first_'+$id}}\"><p>A</p></label>\n    <label class=\" toggle_4\" for=\"{{'second_'+$id}}\"><p>B</p></label>\n    <label class=\" toggle_4\" for=\"{{'third_'+$id}}\"><p>C</p></label>\n    <label class=\" toggle_4\" for=\"{{'fourth_'+$id}}\"><p>D</p></label>\n  </div>\n  \n</div>\n\n<script>\n(function(scope) {\n    scope.incomingChange = false\n    scope.$watch('value', function(value) {\n        switch(value){\n            case 'first':{\n                $(\"#slider_\"+scope.$id).css(\"left\", \"3px\")\n                break\n            }\n            case 'second':{\n                $(\"#slider_\"+scope.$id).css(\"left\", \"25%\")\n                break\n            }\n            case 'third':{\n                $(\"#slider_\"+scope.$id).css(\"left\", \"50%\")\n                break\n            }\n            case 'fourth':{\n                $(\"#slider_\"+scope.$id).css(\"left\", \"74%\")\n                break\n            }\n        }\n        if(scope.incomingChange == true){\n            scope.incomingChange = false\n            return\n        }\n        if(!value){\n            return\n        }\n        scope.send({payload: value})\n       \n    });\n    scope.$watch('msg', function(msg) {\n        if(msg){\n            if(scope.value != msg.payload){\n                scope.incomingChange = true\n                scope.value = msg.payload\n            }\n            \n        }\n    });\n})(scope);\n</script>\n  \n","storeOutMessages":true,"fwdInMessages":true,"resendOnRefresh":true,"templateScope":"local","x":350,"y":360,"wires":[["51fb29aa.c34508"]]},{"id":"51fb29aa.c34508","type":"debug","z":"ca5421c2.cc3a5","name":"Selected option","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":560,"y":360,"wires":[]},{"id":"32d36a7c.a9e9c6","type":"inject","z":"ca5421c2.cc3a5","name":"Select first","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"first","payloadType":"str","x":120,"y":320,"wires":[["5ec61ac2.9a01c4"]]},{"id":"8878f3d7.188a4","type":"inject","z":"ca5421c2.cc3a5","name":"Select second","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"second","payloadType":"str","x":110,"y":360,"wires":[["5ec61ac2.9a01c4"]]},{"id":"3824b305.e6cafc","type":"inject","z":"ca5421c2.cc3a5","name":"Select third","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"third","payloadType":"str","x":120,"y":400,"wires":[["5ec61ac2.9a01c4"]]},{"id":"a71786e7.e950d8","type":"inject","z":"ca5421c2.cc3a5","name":"Select fourth","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"fourth","payloadType":"str","x":110,"y":440,"wires":[["5ec61ac2.9a01c4"]]},{"id":"f754f953.54c708","type":"ui_template","z":"ca5421c2.cc3a5","group":"9f2da61.3353758","name":"OnOffToggle","order":4,"width":6,"height":1,"format":"<div id=\"{{'swc_'+$id}}\" class=\"switchwrapper\">\n  <div class=\"toggle_radio\">\n    <input type=\"radio\" class=\"toggle_option\" id=\"{{'first_'+$id}}\" ng-model=\"value\" value=\"0\">\n    <input type=\"radio\" class=\"toggle_option\" id=\"{{'second_'+$id}}\" ng-model=\"value\" value=\"1\">\n    \n    <div id=\"{{'slider_'+$id}}\" class=\"toggle_option_slider toggle_2\"></div>\n    <label class=\" toggle_2\" for=\"{{'first_'+$id}}\"><p>Off</p></label>\n    <label class=\" toggle_2\" for=\"{{'second_'+$id}}\"><p>On</p></label>\n\n  </div>\n  \n</div>\n\n<script>\n(function(scope) {\n    scope.incomingChange = false\n    scope.$watch('value', function(value) {\n        switch(value){\n            case '0':{\n                $(\"#slider_\"+scope.$id).css(\"left\", \"2px\")\n                break\n            }\n            case '1':{\n                $(\"#slider_\"+scope.$id).css(\"left\", \"49%\")\n                break\n            }\n\n        }\n        if(scope.incomingChange == true){\n            scope.incomingChange = false\n            return\n        }\n        if(!value){\n            return\n        }\n        scope.send({payload: value})\n       \n    });\n    scope.$watch('msg', function(msg) {\n        if(msg){\n            if(scope.value != msg.payload){\n                scope.incomingChange = true\n                scope.value = msg.payload\n            }\n            \n        }\n    });\n})(scope);\n</script>\n  \n","storeOutMessages":true,"fwdInMessages":true,"resendOnRefresh":true,"templateScope":"local","x":350,"y":580,"wires":[[]],"info":"Off = 0\nOn = 1"},{"id":"4faa61eb.351d9","type":"ui_template","z":"ca5421c2.cc3a5","group":"9f2da61.3353758","name":"AutoManToggle","order":4,"width":6,"height":1,"format":"<div id=\"{{'swc_'+$id}}\" class=\"switchwrapper\">\n  <div class=\"toggle_radio\">\n    <input type=\"radio\" class=\"toggle_option\" id=\"{{'first_'+$id}}\" ng-model=\"value\" value=\"1\">\n    <input type=\"radio\" class=\"toggle_option\" id=\"{{'second_'+$id}}\" ng-model=\"value\" value=\"0\">\n    \n    <div id=\"{{'slider_'+$id}}\" class=\"toggle_option_slider toggle_2\"></div>\n    <label class=\" toggle_2\" for=\"{{'first_'+$id}}\"><p>Auto</p></label>\n    <label class=\" toggle_2\" for=\"{{'second_'+$id}}\"><p>Man</p></label>\n\n  </div>\n  \n</div>\n\n<script>\n(function(scope) {\n    scope.incomingChange = false\n    scope.$watch('value', function(value) {\n        switch(value){\n            case '1':{\n                $(\"#slider_\"+scope.$id).css(\"left\", \"2px\")\n                break\n            }\n            case '0':{\n                $(\"#slider_\"+scope.$id).css(\"left\", \"49%\")\n                break\n            }\n\n        }\n        if(scope.incomingChange == true){\n            scope.incomingChange = false\n            return\n        }\n        if(!value){\n            return\n        }\n        scope.send({payload: value})\n       \n    });\n    scope.$watch('msg', function(msg) {\n        if(msg){\n            if(scope.value != msg.payload){\n                scope.incomingChange = true\n                scope.value = msg.payload\n            }\n            \n        }\n    });\n})(scope);\n</script>\n  \n","storeOutMessages":true,"fwdInMessages":true,"resendOnRefresh":true,"templateScope":"local","x":360,"y":620,"wires":[[]],"info":"Manual = 0\nAuto = 1\n\nHowever, Auto is placed in the left most position as it is often the default"},{"id":"803fb828.db1e78","type":"comment","z":"ca5421c2.cc3a5","name":"Default Multi-Way Switch Templates","info":"Default Multi-Way Switch Templates","x":160,"y":80,"wires":[]},{"id":"3fcdd54.9f0f02a","type":"comment","z":"ca5421c2.cc3a5","name":"Automation - Command and State Toggle Examples","info":"Automation - Command and State Toggle Examples","x":210,"y":480,"wires":[]},{"id":"a1a04257.c99ee","type":"comment","z":"ca5421c2.cc3a5","name":"Automation - 2 State Toggles","info":"Automation - Command and State Toggle Examples","x":160,"y":520,"wires":[]},{"id":"6357e025.937f","type":"ui_template","z":"ca5421c2.cc3a5","group":"9f2da61.3353758","name":"OnAutoOffToggle","order":4,"width":6,"height":1,"format":"<div id=\"{{'swc_'+$id}}\" class=\"switchwrapper\">\n  <div class=\"toggle_radio\">\n    <input type=\"radio\" class=\"toggle_option toggle_3\" id=\"{{'first_'+$id}}\" ng-model=\"value\" value=\"0\">\n    <input type=\"radio\" class=\"toggle_option toggle_3\" id=\"{{'second_'+$id}}\" ng-model=\"value\" value=\"-1\">\n    <input type=\"radio\" class=\"toggle_option toggle_3\" id=\"{{'third_'+$id}}\" ng-model=\"value\" value=\"1\">\n    <div id=\"{{'slider_'+$id}}\" class=\"toggle_option_slider toggle_3\"></div>\n    <label class=\" toggle_3\" for=\"{{'first_'+$id}}\"><p>Off</p></label>\n    <label class=\" toggle_3\" for=\"{{'second_'+$id}}\"><p>Auto</p></label>\n    <label class=\" toggle_3\" for=\"{{'third_'+$id}}\"><p>On</p></label>\n  </div>\n  \n</div>\n\n<script>\n(function(scope) {\n    scope.incomingChange = false\n    scope.$watch('value', function(value) {\n        switch(value){\n            case '0':{\n                $(\"#slider_\"+scope.$id).css(\"left\", \"2px\")\n                break\n            }\n            case '-1':{\n                $(\"#slider_\"+scope.$id).css(\"left\", \"33%\")\n                break\n            }\n            case '1':{\n                $(\"#slider_\"+scope.$id).css(\"left\", \"66%\")\n                break\n            }\n        }\n        if(scope.incomingChange == true){\n            scope.incomingChange = false\n            return\n        }\n        if(!value){\n            return\n        }\n        scope.send({payload: value})\n       \n    });\n    scope.$watch('msg', function(msg) {\n        if(msg){\n            if(scope.value != msg.payload){\n                scope.incomingChange = true\n                scope.value = msg.payload\n            }\n            \n        }\n    });\n})(scope);\n</script>\n  \n","storeOutMessages":true,"fwdInMessages":true,"resendOnRefresh":true,"templateScope":"local","x":370,"y":740,"wires":[[]],"info":"Off = 0\nOn = 1\nAuto = -1\n\nMany Device FacePlates will arrange these selections as Off - Auto - On, with Auto being the center and preferred position."},{"id":"87263b06.9f4b78","type":"comment","z":"ca5421c2.cc3a5","name":"Automation - 3 State Toggles","info":"Automation - Command and State Toggle Examples","x":160,"y":680,"wires":[]},{"id":"c8bb006d.cdbc5","type":"ui_template","z":"ca5421c2.cc3a5","group":"9f2da61.3353758","name":"AutoManTrakToggle","order":4,"width":6,"height":1,"format":"<div id=\"{{'swc_'+$id}}\" class=\"switchwrapper\">\n  <div class=\"toggle_radio\">\n    <input type=\"radio\" class=\"toggle_option toggle_3\" id=\"{{'first_'+$id}}\" ng-model=\"value\" value=\"0\">\n    <input type=\"radio\" class=\"toggle_option toggle_3\" id=\"{{'second_'+$id}}\" ng-model=\"value\" value=\"1\">\n    <input type=\"radio\" class=\"toggle_option toggle_3\" id=\"{{'third_'+$id}}\" ng-model=\"value\" value=\"-1\">\n    <div id=\"{{'slider_'+$id}}\" class=\"toggle_option_slider toggle_3\"></div>\n    <label class=\" toggle_3\" for=\"{{'first_'+$id}}\"><p>Auto</p></label>\n    <label class=\" toggle_3\" for=\"{{'second_'+$id}}\"><p>Man</p></label>\n    <label class=\" toggle_3\" for=\"{{'third_'+$id}}\"><p>Trak</p></label>\n  </div>\n  \n</div>\n\n<script>\n(function(scope) {\n    scope.incomingChange = false\n    scope.$watch('value', function(value) {\n        switch(value){\n            case '0':{\n                $(\"#slider_\"+scope.$id).css(\"left\", \"2px\")\n                break\n            }\n            case '1':{\n                $(\"#slider_\"+scope.$id).css(\"left\", \"33%\")\n                break\n            }\n            case '-1':{\n                $(\"#slider_\"+scope.$id).css(\"left\", \"66%\")\n                break\n            }\n        }\n        if(scope.incomingChange == true){\n            scope.incomingChange = false\n            return\n        }\n        if(!value){\n            return\n        }\n        scope.send({payload: value})\n       \n    });\n    scope.$watch('msg', function(msg) {\n        if(msg){\n            if(scope.value != msg.payload){\n                scope.incomingChange = true\n                scope.value = msg.payload\n            }\n            \n        }\n    });\n})(scope);\n</script>\n  \n","storeOutMessages":true,"fwdInMessages":true,"resendOnRefresh":true,"templateScope":"local","x":380,"y":780,"wires":[[]],"info":"Auto = 1\nManual = 0\nTrack = -1\n\nThese options are listed in preferred order, rather than value order\n\nTrack is a Control Mode that is usually set by some event trigger.. for instance a heating loop where you are controlling a valve but you've suddenly lost a fan or pump..... You may want that valve to track to another variable (bumpless tranfers) or a predefined value (safety). Usually, you do not select a Track Mode... However, it might be that some will use this Toggle to display state instead of issueing commands... So it's here. However, it is good practice to use the toggle to issue commmands... and some other indication (test box) be used to show actual state.."},{"id":"6257e76f.d50d08","type":"ui_template","z":"ca5421c2.cc3a5","group":"9f2da61.3353758","name":"LocRemTrakToggle","order":4,"width":6,"height":1,"format":"<div id=\"{{'swc_'+$id}}\" class=\"switchwrapper\">\n  <div class=\"toggle_radio\">\n    <input type=\"radio\" class=\"toggle_option toggle_3\" id=\"{{'first_'+$id}}\" ng-model=\"value\" value=\"0\">\n    <input type=\"radio\" class=\"toggle_option toggle_3\" id=\"{{'second_'+$id}}\" ng-model=\"value\" value=\"1\">\n    <input type=\"radio\" class=\"toggle_option toggle_3\" id=\"{{'third_'+$id}}\" ng-model=\"value\" value=\"-1\">\n    <div id=\"{{'slider_'+$id}}\" class=\"toggle_option_slider toggle_3\"></div>\n    <label class=\" toggle_3\" for=\"{{'first_'+$id}}\"><p>Loc</p></label>\n    <label class=\" toggle_3\" for=\"{{'second_'+$id}}\"><p>Rem</p></label>\n    <label class=\" toggle_3\" for=\"{{'third_'+$id}}\"><p>Trak</p></label>\n  </div>\n  \n</div>\n\n<script>\n(function(scope) {\n    scope.incomingChange = false\n    scope.$watch('value', function(value) {\n        switch(value){\n            case '0':{\n                $(\"#slider_\"+scope.$id).css(\"left\", \"2px\")\n                break\n            }\n            case '1':{\n                $(\"#slider_\"+scope.$id).css(\"left\", \"33%\")\n                break\n            }\n            case '-1':{\n                $(\"#slider_\"+scope.$id).css(\"left\", \"66%\")\n                break\n            }\n        }\n        if(scope.incomingChange == true){\n            scope.incomingChange = false\n            return\n        }\n        if(!value){\n            return\n        }\n        scope.send({payload: value})\n       \n    });\n    scope.$watch('msg', function(msg) {\n        if(msg){\n            if(scope.value != msg.payload){\n                scope.incomingChange = true\n                scope.value = msg.payload\n            }\n            \n        }\n    });\n})(scope);\n</script>\n  \n","storeOutMessages":true,"fwdInMessages":true,"resendOnRefresh":true,"templateScope":"local","x":370,"y":820,"wires":[[]],"info":"Local = 0\nRemote = 1\nTrack = -1\n\nThese options are listed in preferred order, rather than value order\n\nTrack is a Setpoint Mode that is usually set by some event trigger (i.e. fan failure, pump failure).. The idea is in complex cascading control loops, using a track mode for the setpoint can make for bumpless tranfers when the event that caused the trigger is completed. Usually, you do not select a Track Mode... However, it might be that some will use this Toggle to display setpoint mode states instead of issueing a setpoint mode... So it's here. However, it is good practice to use the toggle to issue commmands... and some other indication (text box) be used to show actual state.."},{"id":"33d28ff4.5cecd","type":"comment","z":"ca5421c2.cc3a5","name":"Automation - 4 State Toggles","info":"Automation - Command and State Toggle Examples","x":160,"y":880,"wires":[]},{"id":"179542c8.47d61d","type":"ui_template","z":"ca5421c2.cc3a5","group":"9f2da61.3353758","name":"VesselCmndToggle","order":4,"width":"0","height":"0","format":"<div id=\"{{'swc_'+$id}}\" class=\"switchwrapper\">\n  <div class=\"toggle_radio\">\n    <input type=\"radio\" class=\"toggle_option\" id=\"{{'first_'+$id}}\" ng-model=\"value\" value=\"0\">\n    <input type=\"radio\" class=\"toggle_option\" id=\"{{'second_'+$id}}\" ng-model=\"value\" value=\"1\">\n    <input type=\"radio\" class=\"toggle_option\" id=\"{{'third_'+$id}}\" ng-model=\"value\" value=\"2\">\n    <input type=\"radio\" class=\"toggle_option\" id=\"{{'fourth_'+$id}}\" ng-model=\"value\" value=\"3\">\n    <div id=\"{{'slider_'+$id}}\" class=\"toggle_option_slider toggle_4\"></div>\n    <label class=\" toggle_4\" for=\"{{'first_'+$id}}\"><p>Stndby</p></label>\n    <label class=\" toggle_4\" for=\"{{'second_'+$id}}\"><p>Fill</p></label>\n    <label class=\" toggle_4\" for=\"{{'third_'+$id}}\"><p>Recirc</p></label>\n    <label class=\" toggle_4\" for=\"{{'fourth_'+$id}}\"><p>Drain</p></label>\n  </div>\n  \n</div>\n\n<script>\n(function(scope) {\n    scope.incomingChange = false\n    scope.$watch('value', function(value) {\n        switch(value){\n            case '0':{\n                $(\"#slider_\"+scope.$id).css(\"left\", \"2px\")\n                break\n            }\n            case '1':{\n                $(\"#slider_\"+scope.$id).css(\"left\", \"25%\")\n                break\n            }\n            case '2':{\n                $(\"#slider_\"+scope.$id).css(\"left\", \"50%\")\n                break\n            }\n            case '3':{\n                $(\"#slider_\"+scope.$id).css(\"left\", \"74%\")\n                break\n            }\n        }\n        if(scope.incomingChange == true){\n            scope.incomingChange = false\n            return\n        }\n        if(!value){\n            return\n        }\n        scope.send({payload: value})\n       \n    });\n    scope.$watch('msg', function(msg) {\n        if(msg){\n            if(scope.value != msg.payload){\n                scope.incomingChange = true\n                scope.value = msg.payload\n            }\n            \n        }\n    });\n})(scope);\n</script>\n  \n","storeOutMessages":true,"fwdInMessages":true,"resendOnRefresh":true,"templateScope":"local","x":370,"y":940,"wires":[[]],"info":"This example is different modes/operations occuring in a vessel\nPerhaps you Fill it, Drain it, Recirculate the Contents, or Place it in Standby..\n\nHere we have Standby=0, Fill=1, Recirc=2, Drain=3 \n\nOther option might be Transfer in lieu of Drain"},{"id":"9f2da61.3353758","type":"ui_group","name":"AutomationExamples","tab":"d6d3c358.5fb46","order":1,"disp":true,"width":"12","collapse":false},{"id":"e2c488d6.e06ad8","type":"ui_group","name":"Flight","tab":"d6d3c358.5fb46","order":2,"disp":true,"width":"6","collapse":false},{"id":"d6d3c358.5fb46","type":"ui_tab","name":"UIToggles","icon":"radio_button_checked","order":11,"disabled":false,"hidden":false}]

Flow Info

Created 3 years, 5 months ago
Rating: not yet rated

Actions

Rate:

Node Types

Core
  • comment (x5)
  • debug (x3)
  • inject (x9)
Other
  • tab (x1)
  • ui_group (x2)
  • ui_tab (x1)
  • ui_template (x10)

Tags

  • Toggle
  • Multi-State
  • FishNook
  • SonoraTechnical
  • Dashboard
Copy this flow JSON to your clipboard and then import into Node-RED using the Import From > Clipboard (Ctrl-I) menu option