Is there an example of ui template toggle switch implementation - true/false toggle button (not radio) with css color change to indicate state? Thanks.
Maybe this can help you : multistate-switch
Thanks, but I a looking for UI template implementation of on/off toggle switches - I recall seeing it posted in the forum, but can't find the thread.
Angular.js Material is your friend:
https://material.angularjs.org/latest/demo/switch
This would be an implementation by the template node:
[
{
"id": "ee8099c2e47c91c1",
"type": "tab",
"label": "Flow 1",
"disabled": false,
"info": "",
"env": []
},
{
"id": "2dc4a04ef258f978",
"type": "ui_template",
"z": "ee8099c2e47c91c1",
"group": "589e25a60e4fbcd4",
"name": "",
"order": 0,
"width": 0,
"height": 0,
"format": "<div class=\"inset\" ng-cloak>\n <md-switch id=\"switch1\" ng-model=\"data.cb1\" aria-label=\"Switch 1\" ng-change=\"onChange(data.cb1)\">\n Switch 1: {{ data.cb1 }}\n </md-switch>\n</div>\n\n<script>\n (function(scope) {\n scope.data = {\n cb1: true,\n };\n \n scope.onChange = function(cbState) {\n scope.send({payload: cbState, topic: \"switch changed\"});\n };\n \n //handle arriving messages\n scope.$watch('msg', function(msg) {\n if(msg !== undefined && msg !== null){\n try{\n switch(msg.topic) {\n case \"switch\":\n scope.$applyAsync(function(){\n scope.data.cb1 = !scope.data.cb1;\n });\n scope.onChange(scope.data.cb1);\n break;\n \n default:\n break;\n }\n }catch(err){\n console.error(err);\n }\n }\n });\n })(scope);\n</script>",
"storeOutMessages": true,
"fwdInMessages": true,
"resendOnRefresh": true,
"templateScope": "local",
"className": "",
"x": 1240,
"y": 260,
"wires": [
[
"61fdddc14f55588e"
]
]
},
{
"id": "e0085ef7a2e20493",
"type": "inject",
"z": "ee8099c2e47c91c1",
"name": "",
"props": [
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "switch",
"x": 890,
"y": 260,
"wires": [
[
"2dc4a04ef258f978"
]
]
},
{
"id": "61fdddc14f55588e",
"type": "debug",
"z": "ee8099c2e47c91c1",
"name": "debug 1",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "true",
"targetType": "full",
"statusVal": "",
"statusType": "auto",
"x": 1540,
"y": 260,
"wires": []
},
{
"id": "589e25a60e4fbcd4",
"type": "ui_group",
"name": "Group 1",
"tab": "29d816a6f2669734",
"order": 1,
"disp": true,
"width": 6
},
{
"id": "29d816a6f2669734",
"type": "ui_tab",
"name": "Tab 1",
"icon": "dashboard",
"order": 1
}
]
Thanks very much!
Thanks. I need to combine multiple switch in one template. I added them as follows, but also need to to adjust the function to be able to identify which switch is sending msg. How do I make the topic unique to each switch?
<div style="margin-top: 0px" layout="row">
<div class="myswitch" ng-cloak>
<md-switch id="B" class="myswitch" ng-model="data.b" aria-label="B" ng-change="onChange(data.b)">B</md-switch>
</div>
<div class="myswitch" ng-cloak>
<md-switch id="G" class="myswitch" ng-model="data.g" aria-label="G" ng-change="onChange(data.g)">G</md-switch>
</div>
</div>
<script>
(function(scope) {
scope.data = {
b: true,
g: false,
};
scope.onChange = function(cbState) {
scope.send({payload: cbState, topic: "switch changed"});
};
//handle arriving messages
scope.$watch('msg', function(msg) {
if(msg !== undefined && msg !== null){
try{
switch(msg.topic) {
case "b":
scope.$applyAsync(function(){
scope.data.b = !scope.data.b;
});
scope.onChange(scope.data.b);
break;
case "g":
scope.$applyAsync(function(){
scope.data.g = !scope.data.g;
});
scope.onChange(scope.data.g);
break;
default:
break;
}
}catch(err){
console.error(err);
}
}
});
})(scope);
</script>
Thanks again ...
Also, I see how that you send an input msg.topic to toggle the switch state - what needs to be adjusted to be able set the state by specifying true/false for each switch (rather than toggle).
I managed to get the output separated using ng-true-value (instead of topic). However, still unable to properly set state programmatically - i.e. send msg.topic = "b" and msg.payload = true
<div style="margin-left: 7px" ng-cloak>
<md-switch id="B" ng-model="data.b" aria-label="B" ng-true-value="'b_on'" ng-false-value="'b_off'" ng-change="onChange(data.b)">B</md-switch>
</div>
<div style="margin-left: 5px" ng-cloak>
<md-switch id="G" ng-model="data.g" aria-label="G" ng-true-value="'g_on'" ng-false-value="'g_off'" ng-change="onChange(data.g)">G</md-switch>
</div>
<div style="margin-left: 15px" ng-cloak>
<md-switch id="S" ng-model="data.s" aria-label="G" ng-true-value="'s_on'" ng-false-value="'s_off'" ng-change="onChange(data.s)">SIM</md-switch>
</div>
</div>
<script>
(function(scope) {
scope.data = {
b: 'b_on',
g: 'g_off',
s: 's_on'
};
scope.onChange = function(cbState) {
scope.send({payload: cbState, topic: "switch" });
};
//handle arriving messages
scope.$watch('msg', function(msg) {
if(msg !== undefined && msg !== null){
try{
switch(msg.topic) {
case "b":
scope.$applyAsync(function(){
scope.data.b = !scope.data.b;
});
scope.onChange(scope.data.b);
break;
case "g":
scope.$applyAsync(function(){
scope.data.g = !scope.data.g;
});
scope.onChange(scope.data.g);
break;
case "s":
scope.$applyAsync(function(){
scope.data.s = !scope.data.s;
});
scope.onChange(scope.data.s);
break;
default:
break;
}
}catch(err){
console.error(err);
}
}
});
})(scope);
</script>
In your switch case statement turn
scope.data.g = !scope.data.g;
to
scope.data.g = msg.payload;
The whole flow:
[
{
"id": "ee8099c2e47c91c1",
"type": "tab",
"label": "Flow 1",
"disabled": false,
"info": "",
"env": []
},
{
"id": "61fdddc14f55588e",
"type": "debug",
"z": "ee8099c2e47c91c1",
"name": "debug 1",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "true",
"targetType": "full",
"statusVal": "",
"statusType": "auto",
"x": 1580,
"y": 440,
"wires": []
},
{
"id": "2b60c3632b00a3c1",
"type": "ui_template",
"z": "ee8099c2e47c91c1",
"group": "589e25a60e4fbcd4",
"name": "3 Switches",
"order": 1,
"width": 0,
"height": 0,
"format": "<div style=\"margin-left: 7px\" ng-cloak>\n <md-switch id=\"B\" ng-model=\"data.b\" aria-label=\"B\" ng-true-value=\"'b_on'\" ng-false-value=\"'b_off'\"\n ng-change=\"onChange(data.b)\">B</md-switch>\n</div>\n<div style=\"margin-left: 5px\" ng-cloak>\n <md-switch id=\"G\" ng-model=\"data.g\" aria-label=\"G\" ng-true-value=\"'g_on'\" ng-false-value=\"'g_off'\"\n ng-change=\"onChange(data.g)\">G</md-switch>\n</div>\n<div style=\"margin-left: 15px\" ng-cloak>\n <md-switch id=\"S\" ng-model=\"data.s\" aria-label=\"G\" ng-true-value=\"'s_on'\" ng-false-value=\"'s_off'\"\n ng-change=\"onChange(data.s)\">SIM</md-switch>\n</div>\n\n</div>\n\n<script>\n (function(scope) {\n scope.data = {\n b: 'b_on',\n g: 'g_off',\n s: 's_on'\n };\n \n scope.onChange = function(cbState) {\n scope.send({payload: cbState, topic: \"switch\" });\n };\n \n //handle arriving messages\n scope.$watch('msg', function(msg) {\n if(msg !== undefined && msg !== null){\n try{\n switch(msg.topic) {\n case \"b\":\n scope.$applyAsync(function(){\n scope.data.b = msg.payload;\n });\n scope.onChange(scope.data.b);\n break;\n case \"g\":\n scope.$applyAsync(function(){\n scope.data.g = msg.payload;\n });\n scope.onChange(scope.data.g);\n break; \n case \"s\":\n scope.$applyAsync(function(){\n scope.data.s = msg.payload;\n });\n scope.onChange(scope.data.s);\n break; \n\n default:\n break;\n }\n }catch(err){\n console.error(err);\n }\n }\n });\n })(scope);\n</script>",
"storeOutMessages": true,
"fwdInMessages": true,
"resendOnRefresh": true,
"templateScope": "local",
"className": "",
"x": 1390,
"y": 440,
"wires": [
[
"61fdddc14f55588e"
]
]
},
{
"id": "2fd58ee7ddbbaf1e",
"type": "inject",
"z": "ee8099c2e47c91c1",
"name": "B On",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "b",
"payload": "b_on",
"payloadType": "str",
"x": 830,
"y": 120,
"wires": [
[
"2b60c3632b00a3c1"
]
]
},
{
"id": "fbdb1f32eceb4ee1",
"type": "inject",
"z": "ee8099c2e47c91c1",
"name": "B Off",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "b",
"payload": "b_off",
"payloadType": "str",
"x": 830,
"y": 160,
"wires": [
[
"2b60c3632b00a3c1"
]
]
},
{
"id": "59b9a0d251d4cfac",
"type": "change",
"z": "ee8099c2e47c91c1",
"name": "Boolean to On/Off",
"rules": [
{
"t": "set",
"p": "payload",
"pt": "msg",
"to": "$type(payload)=\"boolean\"?payload:undefined\t\t",
"tot": "jsonata"
},
{
"t": "set",
"p": "topic",
"pt": "msg",
"to": "$type(payload)=\"boolean\"?topic:undefined",
"tot": "jsonata"
},
{
"t": "set",
"p": "payload",
"pt": "msg",
"to": "$replace($string(payload, false), \"true\", topic&\"_on\")\t\t",
"tot": "jsonata"
},
{
"t": "set",
"p": "payload",
"pt": "msg",
"to": "$replace($string(payload, false), \"false\", topic&\"_off\")",
"tot": "jsonata"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 1050,
"y": 640,
"wires": [
[
"2b60c3632b00a3c1"
]
]
},
{
"id": "c7ef7f545b05fe3b",
"type": "inject",
"z": "ee8099c2e47c91c1",
"name": "G On",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "g",
"payload": "true",
"payloadType": "bool",
"x": 830,
"y": 620,
"wires": [
[
"59b9a0d251d4cfac"
]
]
},
{
"id": "b1abc0ba314d4718",
"type": "inject",
"z": "ee8099c2e47c91c1",
"name": "G Off",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "g",
"payload": "false",
"payloadType": "bool",
"x": 830,
"y": 660,
"wires": [
[
"59b9a0d251d4cfac"
]
]
},
{
"id": "fa143732d4d9f774",
"type": "inject",
"z": "ee8099c2e47c91c1",
"name": "SIM On",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "s",
"payload": "s_on",
"payloadType": "str",
"x": 830,
"y": 320,
"wires": [
[
"2b60c3632b00a3c1"
]
]
},
{
"id": "f723b9390c0fc2bd",
"type": "inject",
"z": "ee8099c2e47c91c1",
"name": "SIM Off",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "s",
"payload": "s_off",
"payloadType": "str",
"x": 830,
"y": 360,
"wires": [
[
"2b60c3632b00a3c1"
]
]
},
{
"id": "6196b6c5f1039646",
"type": "inject",
"z": "ee8099c2e47c91c1",
"name": "G On",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "g",
"payload": "g_on",
"payloadType": "str",
"x": 830,
"y": 220,
"wires": [
[
"2b60c3632b00a3c1"
]
]
},
{
"id": "3538dc1b1d12b6c4",
"type": "inject",
"z": "ee8099c2e47c91c1",
"name": "G Off",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "g",
"payload": "g_off",
"payloadType": "str",
"x": 830,
"y": 260,
"wires": [
[
"2b60c3632b00a3c1"
]
]
},
{
"id": "3e4cbc25eb869f3d",
"type": "inject",
"z": "ee8099c2e47c91c1",
"name": "B On",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "b",
"payload": "true",
"payloadType": "bool",
"x": 830,
"y": 520,
"wires": [
[
"59b9a0d251d4cfac"
]
]
},
{
"id": "3f5bab3427c5a4f6",
"type": "inject",
"z": "ee8099c2e47c91c1",
"name": "B Off",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "b",
"payload": "false",
"payloadType": "bool",
"x": 830,
"y": 560,
"wires": [
[
"59b9a0d251d4cfac"
]
]
},
{
"id": "697e8ecf0193f131",
"type": "inject",
"z": "ee8099c2e47c91c1",
"name": "SIM On",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "s",
"payload": "true",
"payloadType": "bool",
"x": 830,
"y": 720,
"wires": [
[
"59b9a0d251d4cfac"
]
]
},
{
"id": "8cff7da8ae756e5c",
"type": "inject",
"z": "ee8099c2e47c91c1",
"name": "SIM Off",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "s",
"payload": "false",
"payloadType": "bool",
"x": 830,
"y": 760,
"wires": [
[
"59b9a0d251d4cfac"
]
]
},
{
"id": "dc3be2b5dd2dd4fd",
"type": "comment",
"z": "ee8099c2e47c91c1",
"name": "with payload as string",
"info": "",
"x": 860,
"y": 80,
"wires": []
},
{
"id": "96520d5d5b1efc91",
"type": "comment",
"z": "ee8099c2e47c91c1",
"name": "with payload as boolean",
"info": "",
"x": 860,
"y": 480,
"wires": []
},
{
"id": "589e25a60e4fbcd4",
"type": "ui_group",
"name": "Group 1",
"tab": "29d816a6f2669734",
"order": 1,
"disp": true,
"width": 6
},
{
"id": "29d816a6f2669734",
"type": "ui_tab",
"name": "Tab 1",
"icon": "dashboard",
"order": 1
}
]
Another way to use boolean values would be to change your code to this:
<div style="margin-left: 7px" ng-cloak>
<md-switch id="B" ng-model="data.b" aria-label="B" ng-true-value=true ng-false-value=false
ng-change="onChange(data.b)">B</md-switch>
</div>
<div style="margin-left: 5px" ng-cloak>
<md-switch id="G" ng-model="data.g" aria-label="G" ng-true-value=true ng-false-value=false
ng-change="onChange(data.g)">G</md-switch>
</div>
<div style="margin-left: 15px" ng-cloak>
<md-switch id="S" ng-model="data.s" aria-label="G" ng-true-value=true ng-false-value=false
ng-change="onChange(data.s)">SIM</md-switch>
</div>
</div>
<script>
(function(scope) {
scope.data = {
b: true,
g: false,
s: true
};
scope.onChange = function(cbState) {
scope.send({payload: cbState, topic: "switch" });
};
//handle arriving messages
scope.$watch('msg', function(msg) {
if(msg !== undefined && msg !== null){
try{
switch(msg.topic) {
case "b":
scope.$applyAsync(function(){
scope.data.b = msg.payload;
});
scope.onChange(scope.data.b);
break;
case "g":
scope.$applyAsync(function(){
scope.data.g = msg.payload;
});
scope.onChange(scope.data.g);
break;
case "s":
scope.$applyAsync(function(){
scope.data.s = msg.payload;
});
scope.onChange(scope.data.s);
break;
default:
break;
}
}catch(err){
console.error(err);
}
}
});
})(scope);
</script>
To filter incoming messages from the output I would turn this feature off in the template node, too:
Thanks, I am having the following issue (which I was experiencing when I was trying to make it work) - on initialization or program triggering I am getting a whole batch of varying/duplicate outputs; when triggered manually the switches are performing as the should (just the single msg). This is a partial snapshot of what happens on initialization
P.S. this is my actual config with CSS to remove slide action (I needed a pushbutton toggle rather than slide switch).
<style>
.sw md-switch.md-checked .md-thumb-container {
transform: none !important;
}
.nr-dashboard-theme .nr-dashboard-template md-switch.md-checked:not([disabled]) .md-thumb {
background-color: #ccc;
}
.nr-dashboard-theme .nr-dashboard-template md-switch .md-bar .myswitch{
background-color: rgba(111, 111, 111, 0.5);
display: none;
}
.nr-dashboard-theme .nr-dashboard-template md-switch.md-checked:not([disabled]) .md-thumb {
background-color: #609f70;
}
.nr-dashboard-theme .nr-dashboard-template md-switch .md-bar {
background-color: rgba(111, 111, 111, 0.5);
display: none;
}
.sw md-switch .md-container {
margin-right: 3px;
width: 25px;
}
.nr-dashboard-theme .nr-dashboard-template md-switch .md-thumb {
background-color: red;
}
md-switch {
align-items: center;
height: 30px;
line-height: 18px;
margin: 0px;
margin-left: inherit;
margin-right: 16px;
}
.sw md-switch .md-label {
font-size: 13px;
color: #728faa;
letter-spacing: 1px;
font-weight: bold;
}
md-switch .md-thumb .md-ripple-container {
display: none;
}
</style>
<div class="sw" style="margin-top: 1px" layout="row">
<div style="margin-left: 7px" ng-cloak>
<md-switch id="B" ng-model="data.b" aria-label="B" ng-true-value="'b_on'" ng-false-value="'b_off'" ng-change="onChange(data.b)">B</md-switch>
</div>
<div style="margin-left: 5px" ng-cloak>
<md-switch id="G" ng-model="data.g" aria-label="G" ng-true-value="'g_on'" ng-false-value="'g_off'" ng-change="onChange(data.g)">G</md-switch>
</div>
<div style="margin-left: 15px" ng-cloak>
<md-switch id="S" ng-model="data.s" aria-label="G" ng-true-value="'s_on'" ng-false-value="'s_off'" ng-change="onChange(data.s)">SIM</md-switch>
</div>
</div>
<script>
(function(scope) {
scope.data = {
b: 'b_on',
g: 'g_off',
s: 's_on'
};
scope.onChange = function(cbState) {
scope.send({payload: cbState, topic: "switch" });
};
//handle arriving messages
scope.$watch('msg', function(msg) {
if(msg !== undefined && msg !== null){
try{
switch(msg.topic) {
case "b":
scope.$applyAsync(function(){
scope.data.b = msg.payload;
});
scope.onChange(scope.data.b);
break;
case "g":
scope.$applyAsync(function(){
scope.data.g = msg.payload;
});
scope.onChange(scope.data.g);
break;
case "s":
scope.$applyAsync(function(){
scope.data.s = msg.payload;
});
scope.onChange(scope.data.s);
break;
default:
break;
}
}catch(err){
console.error(err);
}
}
});
})(scope);
</script>
Still unable to figure out how to prevent all of the duplicate msg ... hoping you could help ... thanks.
Now I had time to do some corrections:
-
I deleted the
scope.$applyAsnc
part which isn't needed when using ng-model -
The use of
scope.$root
to buffer values when switching tabs in your browser app (this way not buffered on server side) -
The use of the
ui-control
node to send messages on every tab change, loss or connection from server side. It helps to supress your duplicate messages. This way you could also do some initialization.
[
{
"id": "ee8099c2e47c91c1",
"type": "tab",
"label": "Flow 1",
"disabled": false,
"info": "",
"env": []
},
{
"id": "61fdddc14f55588e",
"type": "debug",
"z": "ee8099c2e47c91c1",
"name": "debug 1",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "true",
"targetType": "full",
"statusVal": "",
"statusType": "auto",
"x": 1660,
"y": 440,
"wires": []
},
{
"id": "2b60c3632b00a3c1",
"type": "ui_template",
"z": "ee8099c2e47c91c1",
"group": "589e25a60e4fbcd4",
"name": "3 Switches",
"order": 1,
"width": 0,
"height": 0,
"format": "<style>\n .sw md-switch.md-checked .md-thumb-container {\n transform: none !important;\n }\n\n .nr-dashboard-theme .nr-dashboard-template md-switch.md-checked:not([disabled]) .md-thumb {\n background-color: #ccc;\n }\n\n .nr-dashboard-theme .nr-dashboard-template md-switch .md-bar .myswitch {\n background-color: rgba(111, 111, 111, 0.5);\n display: none;\n }\n\n .nr-dashboard-theme .nr-dashboard-template md-switch.md-checked:not([disabled]) .md-thumb {\n background-color: #609f70;\n }\n\n .nr-dashboard-theme .nr-dashboard-template md-switch .md-bar {\n background-color: rgba(111, 111, 111, 0.5);\n display: none;\n }\n\n .sw md-switch .md-container {\n margin-right: 3px;\n width: 25px;\n }\n\n .nr-dashboard-theme .nr-dashboard-template md-switch .md-thumb {\n background-color: red;\n }\n\n md-switch {\n align-items: center;\n height: 30px;\n line-height: 18px;\n margin: 0px;\n margin-left: inherit;\n margin-right: 16px;\n }\n\n .sw md-switch .md-label {\n font-size: 13px;\n color: #728faa;\n letter-spacing: 1px;\n font-weight: bold;\n }\n\n md-switch .md-thumb .md-ripple-container {\n display: none;\n }\n</style>\n\n<div class=\"sw\" style=\"margin-top: 1px\" layout=\"row\">\n\n <div style=\"margin-left: 7px\" ng-cloak>\n <md-switch id=\"B\" ng-model=\"data.b\" aria-label=\"B\" ng-true-value=\"'b_on'\" ng-false-value=\"'b_off'\"\n ng-change=\"onChange(data.b)\">B</md-switch>\n </div>\n <div style=\"margin-left: 5px\" ng-cloak>\n <md-switch id=\"G\" ng-model=\"data.g\" aria-label=\"G\" ng-true-value=\"'g_on'\" ng-false-value=\"'g_off'\"\n ng-change=\"onChange(data.g)\">G</md-switch>\n </div>\n <div style=\"margin-left: 15px\" ng-cloak>\n <md-switch id=\"S\" ng-model=\"data.s\" aria-label=\"G\" ng-true-value=\"'s_on'\" ng-false-value=\"'s_off'\"\n ng-change=\"onChange(data.s)\">SIM</md-switch>\n </div>\n\n</div>\n\n<script>\n (function(scope) {\n\n scope.data = {\n b: scope.$root.B?scope.$root.B:\"b_on\",\n g: scope.$root.G?scope.$root.G:\"g_on\",\n s: scope.$root.S?scope.$root.S:\"s_on\"\n };\n \n scope.onChange = function(cbState) {\n const elementId = cbState.slice(0,1).toUpperCase();\n scope.$root[elementId]=cbState;\n scope.send({topic:elementId, payload: cbState});\n };\n \n //handle arriving messages\n scope.$watch('msg', function(msg1, msg2) {\n console.log(\"message 1: \", msg1);\n console.log(\"message 2: \", msg2)\n \n if(msg1 !== undefined && msg1 !== null && msg2 !== undefined){\n try{\n switch(msg1.topic) {\n case \"b\":\n console.log('message b');\n scope.data.b = msg1.payload;\n scope.onChange(scope.data.b);\n break;\n case \"g\":\n console.log('message g');\n scope.data.g = msg1.payload;\n scope.onChange(scope.data.g);\n break;\n case \"s\":\n console.log('message s');\n scope.data.s = msg1.payload;\n scope.onChange(scope.data.s);\n break;\n case \"change\":\n console.log('change of ' + msg1.name + ' do what ever you want!')\n break\n default:\n break;\n }\n }catch(err){\n console.error(err);\n }\n }\n });\n })(scope);\n</script>",
"storeOutMessages": false,
"fwdInMessages": false,
"resendOnRefresh": false,
"templateScope": "local",
"className": "",
"x": 1390,
"y": 440,
"wires": [
[
"61fdddc14f55588e"
]
]
},
{
"id": "c7ef7f545b05fe3b",
"type": "inject",
"z": "ee8099c2e47c91c1",
"name": "G On",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "g",
"payload": "g_on",
"payloadType": "str",
"x": 790,
"y": 380,
"wires": [
[
"2b60c3632b00a3c1"
]
]
},
{
"id": "b1abc0ba314d4718",
"type": "inject",
"z": "ee8099c2e47c91c1",
"name": "G Off",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "g",
"payload": "g_off",
"payloadType": "str",
"x": 790,
"y": 420,
"wires": [
[
"2b60c3632b00a3c1"
]
]
},
{
"id": "3e4cbc25eb869f3d",
"type": "inject",
"z": "ee8099c2e47c91c1",
"name": "B On",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "b",
"payload": "b_on",
"payloadType": "str",
"x": 790,
"y": 280,
"wires": [
[
"2b60c3632b00a3c1"
]
]
},
{
"id": "3f5bab3427c5a4f6",
"type": "inject",
"z": "ee8099c2e47c91c1",
"name": "B Off",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "b",
"payload": "b_off",
"payloadType": "str",
"x": 790,
"y": 320,
"wires": [
[
"2b60c3632b00a3c1"
]
]
},
{
"id": "697e8ecf0193f131",
"type": "inject",
"z": "ee8099c2e47c91c1",
"name": "SIM On",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "s",
"payload": "s_on",
"payloadType": "str",
"x": 790,
"y": 480,
"wires": [
[
"2b60c3632b00a3c1"
]
]
},
{
"id": "8cff7da8ae756e5c",
"type": "inject",
"z": "ee8099c2e47c91c1",
"name": "SIM Off",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "s",
"payload": "s_off",
"payloadType": "str",
"x": 790,
"y": 520,
"wires": [
[
"2b60c3632b00a3c1"
]
]
},
{
"id": "96520d5d5b1efc91",
"type": "comment",
"z": "ee8099c2e47c91c1",
"name": "with payload as boolean",
"info": "",
"x": 820,
"y": 240,
"wires": []
},
{
"id": "bd86f9187f5b7212",
"type": "ui_ui_control",
"z": "ee8099c2e47c91c1",
"name": "onConnect",
"events": "all",
"x": 770,
"y": 600,
"wires": [
[
"fb38cb759656103d"
]
]
},
{
"id": "d1c52a884a979ca7",
"type": "ui_button",
"z": "ee8099c2e47c91c1",
"name": "",
"group": "2cad8ed8b01011d8",
"order": 0,
"width": 0,
"height": 0,
"passthru": false,
"label": "button",
"tooltip": "",
"color": "",
"bgcolor": "",
"className": "",
"icon": "",
"payload": "",
"payloadType": "str",
"topic": "topic",
"topicType": "msg",
"x": 1390,
"y": 300,
"wires": [
[]
]
},
{
"id": "fb38cb759656103d",
"type": "change",
"z": "ee8099c2e47c91c1",
"name": "",
"rules": [
{
"t": "set",
"p": "topic",
"pt": "msg",
"to": "payload",
"tot": "msg"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 1000,
"y": 600,
"wires": [
[
"2b60c3632b00a3c1"
]
]
},
{
"id": "589e25a60e4fbcd4",
"type": "ui_group",
"name": "Group 1",
"tab": "29d816a6f2669734",
"order": 1,
"disp": true,
"width": 6
},
{
"id": "2cad8ed8b01011d8",
"type": "ui_group",
"name": "Group 1",
"tab": "fdade815ef4911c1",
"order": 1,
"disp": true,
"width": 6
},
{
"id": "29d816a6f2669734",
"type": "ui_tab",
"name": "Tab 1",
"icon": "dashboard",
"order": 1
},
{
"id": "fdade815ef4911c1",
"type": "ui_tab",
"name": "Tab 2",
"icon": "dashboard",
"order": 2
}
]
Thanks. I see that each inject node now has it's own switch topic (which wasn't the case before)? Why is that necessary if all switch states names are unique? I am current setting all of my defaults with one change node. That's not going to be possible if each switch requires it's own topic?
You may of course use one topic e.g. "switch". It's not necessary to use different topics.
Can't figure out what needs to be adjusted in order to be able to use a single topic?
watch this flow it changes with only one topic "switch" and buffers your settings in flow.data
[
{
"id": "ee8099c2e47c91c1",
"type": "tab",
"label": "Flow 1",
"disabled": false,
"info": "",
"env": []
},
{
"id": "61fdddc14f55588e",
"type": "debug",
"z": "ee8099c2e47c91c1",
"name": "debug 1",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "true",
"targetType": "full",
"statusVal": "",
"statusType": "auto",
"x": 1440,
"y": 440,
"wires": []
},
{
"id": "2b60c3632b00a3c1",
"type": "ui_template",
"z": "ee8099c2e47c91c1",
"group": "589e25a60e4fbcd4",
"name": "3 Switches",
"order": 1,
"width": 0,
"height": 0,
"format": "<style>\n .sw md-switch.md-checked .md-thumb-container {\n transform: none !important;\n }\n\n .nr-dashboard-theme .nr-dashboard-template md-switch.md-checked:not([disabled]) .md-thumb {\n background-color: #ccc;\n }\n\n .nr-dashboard-theme .nr-dashboard-template md-switch .md-bar .myswitch {\n background-color: rgba(111, 111, 111, 0.5);\n display: none;\n }\n\n .nr-dashboard-theme .nr-dashboard-template md-switch.md-checked:not([disabled]) .md-thumb {\n background-color: #609f70;\n }\n\n .nr-dashboard-theme .nr-dashboard-template md-switch .md-bar {\n background-color: rgba(111, 111, 111, 0.5);\n display: none;\n }\n\n .sw md-switch .md-container {\n margin-right: 3px;\n width: 25px;\n }\n\n .nr-dashboard-theme .nr-dashboard-template md-switch .md-thumb {\n background-color: red;\n }\n\n md-switch {\n align-items: center;\n height: 30px;\n line-height: 18px;\n margin: 0px;\n margin-left: inherit;\n margin-right: 16px;\n }\n\n .sw md-switch .md-label {\n font-size: 13px;\n color: #728faa;\n letter-spacing: 1px;\n font-weight: bold;\n }\n\n md-switch .md-thumb .md-ripple-container {\n display: none;\n }\n</style>\n\n<div class=\"sw\" style=\"margin-top: 1px\" layout=\"row\">\n <div style=\"margin-left: 7px\" ng-cloak>\n <md-switch id=\"C\" ng-model=\"data.c\" aria-label=\"C\" ng-true-value=\"'c_on'\" ng-false-value=\"'c_off'\"\n ng-change=\"onChange(data.c)\">C</md-switch>\n </div>\n <div style=\"margin-left: 7px\" ng-cloak>\n <md-switch id=\"B\" ng-model=\"data.b\" aria-label=\"B\" ng-true-value=\"'b_on'\" ng-false-value=\"'b_off'\"\n ng-change=\"onChange(data.b)\">B</md-switch>\n </div>\n <div style=\"margin-left: 5px\" ng-cloak>\n <md-switch id=\"G\" ng-model=\"data.g\" aria-label=\"G\" ng-true-value=\"'g_on'\" ng-false-value=\"'g_off'\"\n ng-change=\"onChange(data.g)\">G</md-switch>\n </div>\n <div style=\"margin-left: 15px\" ng-cloak>\n <md-switch id=\"S\" ng-model=\"data.s\" aria-label=\"G\" ng-true-value=\"'s_on'\" ng-false-value=\"'s_off'\"\n ng-change=\"onChange(data.s)\">SIM</md-switch>\n </div>\n\n</div>\n\n<script>\n (function(scope) {\n \n scope.onChange = function(cbState) {\n const elementId = cbState.slice(0,1);\n const elementValue = cbState.slice(2);\n scope.send({topic:elementId, payload: elementValue});\n };\n\n scope.setNewData = function(payload){\n Object.keys(payload).forEach((element) => {\n payload[element] = element + '_' + payload[element];\n });\n scope.data = payload;\n }\n \n //handle arriving messages\n scope.$watch('msg', function(msg1, msg2) {\n if(msg1 !== undefined && msg1 !== null && msg2 !== undefined){\n try{\n switch(msg1.topic) {\n case \"switch\":\n scope.setNewData(msg1.payload);\n case \"change\":\n if(msg1.tab == 0){\n scope.setNewData(msg1.payload);\n } \n break;\n default:\n break;\n }\n }catch(err){\n console.error(err);\n }\n }\n });\n })(scope);\n</script>",
"storeOutMessages": false,
"fwdInMessages": false,
"resendOnRefresh": false,
"templateScope": "local",
"className": "",
"x": 1170,
"y": 440,
"wires": [
[
"61fdddc14f55588e",
"79d81a5fb881c0be"
]
]
},
{
"id": "bd86f9187f5b7212",
"type": "ui_ui_control",
"z": "ee8099c2e47c91c1",
"name": "onConnect",
"events": "all",
"x": 370,
"y": 780,
"wires": [
[
"fb38cb759656103d"
]
]
},
{
"id": "d1c52a884a979ca7",
"type": "ui_button",
"z": "ee8099c2e47c91c1",
"name": "",
"group": "2cad8ed8b01011d8",
"order": 0,
"width": 0,
"height": 0,
"passthru": false,
"label": "button",
"tooltip": "",
"color": "",
"bgcolor": "",
"className": "",
"icon": "",
"payload": "",
"payloadType": "str",
"topic": "topic",
"topicType": "msg",
"x": 1390,
"y": 300,
"wires": [
[]
]
},
{
"id": "fb38cb759656103d",
"type": "change",
"z": "ee8099c2e47c91c1",
"name": "",
"rules": [
{
"t": "set",
"p": "topic",
"pt": "msg",
"to": "payload",
"tot": "msg"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 560,
"y": 780,
"wires": [
[
"a4fc1cadefade6df"
]
]
},
{
"id": "d3b058c0990ce6de",
"type": "inject",
"z": "ee8099c2e47c91c1",
"name": "Switch all",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "switch",
"payload": "{\"c\":\"on\",\"b\":\"on\",\"g\":\"on\",\"s\":\"off\"}",
"payloadType": "json",
"x": 800,
"y": 440,
"wires": [
[
"2b60c3632b00a3c1"
]
]
},
{
"id": "76f9239cd31682d7",
"type": "debug",
"z": "ee8099c2e47c91c1",
"name": "debug 2",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "true",
"targetType": "full",
"statusVal": "",
"statusType": "auto",
"x": 1140,
"y": 780,
"wires": []
},
{
"id": "a4fc1cadefade6df",
"type": "change",
"z": "ee8099c2e47c91c1",
"name": "",
"rules": [
{
"t": "set",
"p": "payload",
"pt": "msg",
"to": "topic=\"change\"?$flowContext(\"data\"):payload\t",
"tot": "jsonata"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 770,
"y": 780,
"wires": [
[
"76f9239cd31682d7",
"2b60c3632b00a3c1"
]
]
},
{
"id": "79d81a5fb881c0be",
"type": "function",
"z": "ee8099c2e47c91c1",
"name": "set flow.data",
"func": "let data = flow.get('data');\ndata[msg.topic]=msg.payload;\nflow.set('data', data);\nreturn",
"outputs": 0,
"noerr": 0,
"initialize": "// Der Code hier wird ausgefĂĽhrt,\n// wenn der Node gestartet wird\nflow.set('data', {\n g:'on',\n b:'on',\n c:'on',\n s:'off'\n})",
"finalize": "",
"libs": [],
"x": 1450,
"y": 540,
"wires": []
},
{
"id": "589e25a60e4fbcd4",
"type": "ui_group",
"name": "Group 1",
"tab": "29d816a6f2669734",
"order": 1,
"disp": true,
"width": 6
},
{
"id": "2cad8ed8b01011d8",
"type": "ui_group",
"name": "Group 1",
"tab": "fdade815ef4911c1",
"order": 1,
"disp": true,
"width": 6
},
{
"id": "29d816a6f2669734",
"type": "ui_tab",
"name": "Tab 1",
"icon": "dashboard",
"order": 1
},
{
"id": "fdade815ef4911c1",
"type": "ui_tab",
"name": "Tab 2",
"icon": "dashboard",
"order": 2
}
]
I get the appropriate msg out when I trigger manually but I don't see anything sent out when I trigger the switch programmatically (except for the switch visually changing state)?
In my last post the data is backed up serverside. You really need to send out remote switched toggles from your browser? If two browsers are open you get the same message twice. I thought you first change the flow.data and then send a remote trigger to every open browser. If you switch in your browser you get the message, change the flow.data and then send a remote message to every else opened browser. If you really need it then in the switch case statement do Something Like:
scope.send({topic:"switch", payload:scope.data})
This is what I would do:
[
{
"id": "ee8099c2e47c91c1",
"type": "tab",
"label": "Flow 1",
"disabled": false,
"info": "",
"env": []
},
{
"id": "2b60c3632b00a3c1",
"type": "ui_template",
"z": "ee8099c2e47c91c1",
"group": "589e25a60e4fbcd4",
"name": "3 Switches",
"order": 1,
"width": 0,
"height": 0,
"format": "<style>\n .sw md-switch.md-checked .md-thumb-container {\n transform: none !important;\n }\n\n .nr-dashboard-theme .nr-dashboard-template md-switch.md-checked:not([disabled]) .md-thumb {\n background-color: #ccc;\n }\n\n .nr-dashboard-theme .nr-dashboard-template md-switch .md-bar .myswitch {\n background-color: rgba(111, 111, 111, 0.5);\n display: none;\n }\n\n .nr-dashboard-theme .nr-dashboard-template md-switch.md-checked:not([disabled]) .md-thumb {\n background-color: #609f70;\n }\n\n .nr-dashboard-theme .nr-dashboard-template md-switch .md-bar {\n background-color: rgba(111, 111, 111, 0.5);\n display: none;\n }\n\n .sw md-switch .md-container {\n margin-right: 3px;\n width: 25px;\n }\n\n .nr-dashboard-theme .nr-dashboard-template md-switch .md-thumb {\n background-color: red;\n }\n\n md-switch {\n align-items: center;\n height: 30px;\n line-height: 18px;\n margin: 0px;\n margin-left: inherit;\n margin-right: 16px;\n }\n\n .sw md-switch .md-label {\n font-size: 13px;\n color: #728faa;\n letter-spacing: 1px;\n font-weight: bold;\n }\n\n md-switch .md-thumb .md-ripple-container {\n display: none;\n }\n</style>\n\n<div class=\"sw\" style=\"margin-top: 1px\" layout=\"row\">\n <div style=\"margin-left: 7px\" ng-cloak>\n <md-switch id=\"C\" ng-model=\"data.c\" aria-label=\"C\" ng-true-value=\"'c_on'\" ng-false-value=\"'c_off'\"\n ng-change=\"onChange(data.c)\">C</md-switch>\n </div>\n <div style=\"margin-left: 7px\" ng-cloak>\n <md-switch id=\"B\" ng-model=\"data.b\" aria-label=\"B\" ng-true-value=\"'b_on'\" ng-false-value=\"'b_off'\"\n ng-change=\"onChange(data.b)\">B</md-switch>\n </div>\n <div style=\"margin-left: 5px\" ng-cloak>\n <md-switch id=\"G\" ng-model=\"data.g\" aria-label=\"G\" ng-true-value=\"'g_on'\" ng-false-value=\"'g_off'\"\n ng-change=\"onChange(data.g)\">G</md-switch>\n </div>\n <div style=\"margin-left: 15px\" ng-cloak>\n <md-switch id=\"S\" ng-model=\"data.s\" aria-label=\"G\" ng-true-value=\"'s_on'\" ng-false-value=\"'s_off'\"\n ng-change=\"onChange(data.s)\">SIM</md-switch>\n </div>\n\n</div>\n\n<script>\n (function(scope) {\n \n scope.onChange = function(cbState) {\n const elementId = cbState.slice(0,1);\n const elementValue = cbState.slice(2);\n scope.send({topic:elementId, payload: elementValue});\n };\n\n scope.setNewData = function(payload){\n Object.keys(payload).forEach((element) => {\n payload[element] = element + '_' + payload[element];\n });\n scope.data = payload;\n }\n \n //handle arriving messages\n scope.$watch('msg', function(msg1, msg2) {\n if(msg1 !== undefined && msg1 !== null && msg2 !== undefined){\n try{\n switch(msg1.topic) {\n case \"switch\":\n scope.setNewData(msg1.payload);\n case \"change\":\n if(msg1.tab == 0){\n scope.setNewData(msg1.payload);\n } \n break;\n default:\n break;\n }\n }catch(err){\n console.error(err);\n }\n }\n });\n })(scope);\n</script>",
"storeOutMessages": false,
"fwdInMessages": false,
"resendOnRefresh": false,
"templateScope": "local",
"className": "",
"x": 1170,
"y": 440,
"wires": [
[
"79d81a5fb881c0be"
]
]
},
{
"id": "bd86f9187f5b7212",
"type": "ui_ui_control",
"z": "ee8099c2e47c91c1",
"name": "onConnect",
"events": "all",
"x": 370,
"y": 780,
"wires": [
[
"fb38cb759656103d"
]
]
},
{
"id": "d1c52a884a979ca7",
"type": "ui_button",
"z": "ee8099c2e47c91c1",
"name": "",
"group": "2cad8ed8b01011d8",
"order": 0,
"width": 0,
"height": 0,
"passthru": false,
"label": "button",
"tooltip": "",
"color": "",
"bgcolor": "",
"className": "",
"icon": "",
"payload": "",
"payloadType": "str",
"topic": "topic",
"topicType": "msg",
"x": 1570,
"y": 300,
"wires": [
[]
]
},
{
"id": "fb38cb759656103d",
"type": "change",
"z": "ee8099c2e47c91c1",
"name": "",
"rules": [
{
"t": "set",
"p": "topic",
"pt": "msg",
"to": "payload",
"tot": "msg"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 560,
"y": 780,
"wires": [
[
"a4fc1cadefade6df"
]
]
},
{
"id": "d3b058c0990ce6de",
"type": "inject",
"z": "ee8099c2e47c91c1",
"name": "Switch all",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "switch",
"payload": "{\"c\":\"on\",\"b\":\"on\",\"g\":\"on\",\"s\":\"off\"}",
"payloadType": "json",
"x": 520,
"y": 440,
"wires": [
[
"3bd4914f4ffe0983"
]
]
},
{
"id": "76f9239cd31682d7",
"type": "debug",
"z": "ee8099c2e47c91c1",
"name": "debug 2",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "true",
"targetType": "full",
"statusVal": "",
"statusType": "auto",
"x": 1140,
"y": 780,
"wires": []
},
{
"id": "a4fc1cadefade6df",
"type": "change",
"z": "ee8099c2e47c91c1",
"name": "",
"rules": [
{
"t": "set",
"p": "payload",
"pt": "msg",
"to": "topic=\"change\"?$flowContext(\"data\"):payload\t",
"tot": "jsonata"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 770,
"y": 780,
"wires": [
[
"76f9239cd31682d7",
"2b60c3632b00a3c1"
]
]
},
{
"id": "79d81a5fb881c0be",
"type": "function",
"z": "ee8099c2e47c91c1",
"name": "set flow.data",
"func": "let data = flow.get('data');\ndata[msg.topic]=msg.payload;\nflow.set('data', data);\nreturn {topic:'switch', payload:data}",
"outputs": 1,
"noerr": 0,
"initialize": "// Der Code hier wird ausgefĂĽhrt,\n// wenn der Node gestartet wird\nflow.set('data', {\n g:'on',\n b:'on',\n c:'on',\n s:'off'\n})",
"finalize": "",
"libs": [],
"x": 1170,
"y": 320,
"wires": [
[
"2b60c3632b00a3c1"
]
]
},
{
"id": "3bd4914f4ffe0983",
"type": "function",
"z": "ee8099c2e47c91c1",
"name": "set flow.data",
"func": "flow.set('data', msg.payload);\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 790,
"y": 440,
"wires": [
[
"2b60c3632b00a3c1"
]
]
},
{
"id": "589e25a60e4fbcd4",
"type": "ui_group",
"name": "Group 1",
"tab": "29d816a6f2669734",
"order": 1,
"disp": true,
"width": 6
},
{
"id": "2cad8ed8b01011d8",
"type": "ui_group",
"name": "Group 1",
"tab": "fdade815ef4911c1",
"order": 1,
"disp": true,
"width": 6
},
{
"id": "29d816a6f2669734",
"type": "ui_tab",
"name": "Tab 1",
"icon": "dashboard",
"order": 1
},
{
"id": "fdade815ef4911c1",
"type": "ui_tab",
"name": "Tab 2",
"icon": "dashboard",
"order": 2
}
]
Not sure what you mean. The main use of the switch is to control certain events from the dashboard (turn switches off/on manually). I also need to be ablet to set them programmatically - set the desired defaults on load (which may change) as well set individual switches based on certain events. You mention "every open browser" - how is that relevant? I' have a lot of different switches, often have the app in multiple browser windows, I've never see any issues (I am not trying to toggle switches on tab change)?