@hazymat wrote:
For anyone interested, here's my progress so far. I have made a UI in Dashboard, with three panels:
- Create / delete / view list of light fixtures
- Create / delete /view list of light zones
- Assign a fixture to a zone (based on @TotallyInformation's "device to room map")
It will save the items to global context, and uses flow context for temp storage of the new values. Also it features validation to ensure all fields were filled in / selected. And of course it checks the ID before saving, to ensure no duplicates.
The IDs for each fixture or zone actually form the object key, so it's nice to look through in context explorer sidebar.
[{"id":"8a93364b.187ff8","type":"comment","z":"705d403b.f2b8","name":"Version 2","info":"\n6/10/2019\n---------\nStart with the things we want to do / make easy for installation\n- Ongoing\n - tweak lighting scenes\n - re-assign roving lights to different zones\n\n- Setup\n - Create fixings from the UI? If we are using DMX we could standardise the\n output message, and set this in the UI as well. Otherwise, what's the\n point? We'd still have to create the thing in Node-RED that sends the\n message to the light. Well, maybe there is a point. Maybe it's just\n useful to see the contents of memory in realtime from the front end,\n as you add devices. We could set the MQTT path from front end as well.\n Then all we'd have to do is set up the device and \"attach\" to the MQTT\n path.\n - Create zones and map fixings to zones\n - Create zone presets\n - List out the zones and mapped fixings\n - List out the zone presets\n\nThen move on to how we want to store stuff in memory.\n- home.light.fixture\n- home.light.zone\n- home.light.preset\n- home.light.mapping.fixture_zone\n- home.light.mapping.preset_zone\n\n\n\nLights are standalone items\nWithin their definition, their location is not set\n\nWe also have room definitions, each room is a simple list of light\nindexes.\n\n\n{\n\t\"lights\" : [\n\t\t{\n\t\t\t\"id\"\t\t\t:\t\"3f_bedroom_ceiling\",\n\t\t\t\"friendly\"\t\t: \t\"3rd floor bedroom ceiling\",\n\t\t\t\"type\"\t\t\t:\t\"dimmable single colour\",\n\t\t\t\"topic\"\t\t\t:\t\"home/light/1\",\n\t\t\t\"defaultstate\"\t:\t20,\n\t\t\t\"currentstate\"\t:\t50\n\t\t},\n\t\t{\n\t\t\t\"id\"\t\t\t:\t\"3f_bedroom_bedside\",\n\t\t\t\"friendly\"\t\t: \t\"3rd floor bedroom bedside\",\n\t\t\t\"type\"\t\t\t:\t\"dimmable single colour\",\n\t\t\t\"topic\"\t\t\t:\t\"home/light/2\",\n\t\t\t\"defaultstate\"\t:\t20,\n\t\t\t\"currentstate\"\t:\t50\n\t\t}\n\t],\n\t\"zones\"\t: [\n\t\t{\n\t\t\t\"id\"\t\t\t: \"3f_bedroom\",\n\t\t\t\"friendly\"\t\t: \"Master bedroom\"\n\t\t\t\"members\"\t\t: [\"3f_bedroom_ceiling\", \"3f_bedroom_bedside\"],\n\t\t\t\"scenes\"\t\t: [\n\t\t\t\t{\n\t\t\t\t\t\"Name\":\"Off\",\n\t\t\t\t\t\"Values\" : [\n\t\t\t\t\t\t\n\t\t\t\t\t]\n\t\t\t\t}\n\t\t\t]\n\t\t},\n\t\t{\n\t\t\t\"id\"\t\t\t: \"3f_living\",\n\t\t\t\"friendly\"\t\t: \"Living Room\",\n\t\t\t\"members\"\t\t: [\"3f_living_ceiling\", \"3f_living_wall\",\"huego1\"]\n\t\t}\n\t]\n}\n\n\n","x":2800,"y":1860,"wires":[]},{"id":"7dc1ca73.ae1494","type":"ui_text_input","z":"705d403b.f2b8","name":"","label":"ID","tooltip":"","group":"1a37e545.435d2b","order":1,"width":3,"height":1,"passthru":false,"mode":"text","delay":"350","topic":"","x":3150,"y":1900,"wires":[["e5afc774.989118"]]},{"id":"e5afc774.989118","type":"change","z":"705d403b.f2b8","name":"","rules":[{"t":"set","p":"newfixture.ID","pt":"flow","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":3420,"y":1900,"wires":[[]]},{"id":"40d1dfbc.93ecb","type":"ui_button","z":"705d403b.f2b8","name":"","group":"1a37e545.435d2b","order":5,"width":1,"height":1,"passthru":false,"label":"Save","tooltip":"","color":"","bgcolor":"","icon":"","payload":"nothing","payloadType":"str","topic":"","x":2810,"y":2020,"wires":[["678588e9.2d24c8"]]},{"id":"7dbff401.90176c","type":"ui_text_input","z":"705d403b.f2b8","name":"","label":"Friendly location","tooltip":"","group":"1a37e545.435d2b","order":2,"width":3,"height":1,"passthru":true,"mode":"text","delay":"1000","topic":"","x":3180,"y":1920,"wires":[["65ce0f79.994dc"]]},{"id":"65ce0f79.994dc","type":"change","z":"705d403b.f2b8","name":"","rules":[{"t":"set","p":"newfixture.friendly_location","pt":"flow","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":3460,"y":1920,"wires":[[]]},{"id":"678588e9.2d24c8","type":"function","z":"705d403b.f2b8","name":"New fixture","func":"// Save new fixture entered in Dashboard to global context\n// Includes form validation and duplicate check\n// and supports clearing the form when done correctly\n\n// check form was filled in properly\nif (typeof flow.get(\"newfixture\") == 'undefined') {\n msg.payload = \"You didn't enter anything - fixture not saved\"\n msg.error = true\n return msg\n}\n\n// assume newfixture object exists (some part of form was filled)\nvar newfixture = flow.get(\"newfixture\")\n\n// if form not complete\nif (typeof newfixture.ID == 'undefined' ||\n newfixture.ID === \"\" ||\n typeof newfixture.friendly_location == 'undefined' ||\n newfixture.friendly_location === \"\" ||\n typeof newfixture.friendly_name == 'undefined' ||\n newfixture.friendly_name === \"\" ||\n typeof newfixture.type == 'undefined' ||\n newfixture.type === \"\") {\n msg.payload = \"Missing info - fixture not saved\"\n msg.error = true\n return msg\n}\n\n// if we've got this far, we have a good new fixture to add\n// create global fixtures object if it's not there\nif (typeof global.get(\"home.light.config.fixtures\") == 'undefined') {\n global.set(\"home.light.config.fixtures\", {})\n}\n\n// get light fixtures object\nvar fixtures = global.get(\"home.light.config.fixtures\")\n\n// does fixture ID exist?\n\nif (fixtures.hasOwnProperty(newfixture.ID)) {\n msg.payload = \"oops, ID already exists\"\n msg.error = true\n return msg\n} else {\n d = new Date().toLocaleDateString(); t = new Date().toLocaleTimeString();\n fixtures[newfixture.ID] = {\n \"friendly_location\": newfixture.friendly_location,\n \"friendly_name\": newfixture.friendly_name,\n \"type\": newfixture.type,\n \"created_date\": d,\n \"created_time\": t\n }\n \n global.set(\"home.light.config.fixtures\",fixtures)\n msg.payload = \"Success - added fixture\"\n msg.error = false\n // now remove item from flow context, otherwise our\n // validation tests above will always pass\n flow.set(\"newfixture\", undefined);\n \n return msg\n}","outputs":1,"noerr":0,"x":2950,"y":2020,"wires":[["13efd823.365108","e05ac37e.8fa61"]]},{"id":"178c7bb6.0832d4","type":"ui_dropdown","z":"705d403b.f2b8","name":"Type","label":"","tooltip":"","place":"Control Type","group":"1a37e545.435d2b","order":4,"width":3,"height":1,"passthru":true,"options":[{"label":"Non Dimmable (Switch)","value":"1_non_dim","type":"str"},{"label":"Dimmable Single Colour","value":"2_single_colour","type":"str"},{"label":"RGB","value":"3_rgb","type":"str"},{"label":"RGBW","value":"4_RGBW","type":"str"},{"label":"RGBWW","value":"5_RGBWW","type":"str"},{"label":"Individually Addressable","value":"6_individually_addressable","type":"str"}],"payload":"","topic":"","x":3150,"y":1960,"wires":[["8715f8dc.f16588"]]},{"id":"8715f8dc.f16588","type":"change","z":"705d403b.f2b8","name":"","rules":[{"t":"set","p":"newfixture.type","pt":"flow","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":3430,"y":1960,"wires":[[]]},{"id":"13efd823.365108","type":"ui_template","z":"705d403b.f2b8","group":"1a37e545.435d2b","name":"New fixture validation","order":6,"width":"8","height":1,"format":"{{msg.payload}}","storeOutMessages":true,"fwdInMessages":false,"templateScope":"local","x":3400,"y":2020,"wires":[[]]},{"id":"efc2b541.5fa958","type":"delay","z":"705d403b.f2b8","name":"","pauseType":"delay","timeout":"5","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":3220,"y":2060,"wires":[["13efd823.365108"]]},{"id":"e05ac37e.8fa61","type":"function","z":"705d403b.f2b8","name":"Success","func":"// Save new fixture: clear form & result if successful\n\nif (!msg.error) {\n msg.payload = \"\"\n return msg;\n}\n","outputs":1,"noerr":0,"x":3060,"y":2060,"wires":[["efc2b541.5fa958","7dc1ca73.ae1494","7dbff401.90176c","178c7bb6.0832d4","9126cc5a.af8f3","c501b125.62688"]]},{"id":"77a3d35d.3ceecc","type":"change","z":"705d403b.f2b8","name":"","rules":[{"t":"set","p":"newfixture.friendly_name","pt":"flow","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":3460,"y":1940,"wires":[[]]},{"id":"9126cc5a.af8f3","type":"ui_text_input","z":"705d403b.f2b8","name":"","label":"Friendly Fitting Type","tooltip":"","group":"1a37e545.435d2b","order":3,"width":3,"height":1,"passthru":true,"mode":"text","delay":"1000","topic":"","x":3200,"y":1940,"wires":[["77a3d35d.3ceecc"]]},{"id":"c501b125.62688","type":"change","z":"705d403b.f2b8","name":"Get","rules":[{"t":"set","p":"fixtures","pt":"msg","to":"home.light.config.fixtures","tot":"global"}],"action":"","property":"","from":"","to":"","reg":false,"x":3210,"y":2120,"wires":[["8c47badb.7f7df8"]]},{"id":"8c47badb.7f7df8","type":"ui_template","z":"705d403b.f2b8","group":"1a37e545.435d2b","name":"Fixtures List","order":7,"width":"12","height":"8","format":"<h3>Register of Light Fixtures</h3>\n\n<table>\n <tr style=\"font-weight:800; background-color:rgba(20,20,20,.6);\">\n <td>ID</td>\n <td>Location</td>\n <td>Name</td>\n <td>Type</td>\n <td>Added</td>\n <td>Delete*</td>\n </tr>\n <tr ng-repeat=\"(key, value) in msg.fixtures\">\n <td>{{key}}</td>\n <td>{{value.friendly_location}}</td>\n <td>{{value.friendly_name}}</td>\n <td>{{value.type}}</td>\n <td style=\"color:#888;\">{{value.created_time}} {{value.created_date}}</td>\n <td><md-button ng-click=\"send({action: 'delete', topic: key})\">Del</md-button></td>\n </tr>\n</table>","storeOutMessages":false,"fwdInMessages":false,"templateScope":"local","x":3350,"y":2120,"wires":[["7dadd06a.5a505"]]},{"id":"7dadd06a.5a505","type":"function","z":"705d403b.f2b8","name":"Set","func":"// get object from memory\n// NB we are using \"bracket notation\" just in case the ID\n// (i.e. the object name, represented here as msg.topic)\n// had a space\n\nif (msg.action == \"delete\") {\n obj = global.get(\"home.light.config.fixtures\")\n delete obj[msg.topic]\n global.set(\"home.light.config.fixtures\", obj)\n}\n\nreturn msg;\n","outputs":1,"noerr":0,"x":3490,"y":2120,"wires":[["c501b125.62688"]]},{"id":"5ea5059b.ac0cac","type":"inject","z":"705d403b.f2b8","name":"Update view","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":3030,"y":2120,"wires":[["c501b125.62688"]]},{"id":"4714f8ee.5b3398","type":"ui_text_input","z":"705d403b.f2b8","name":"","label":"ID","tooltip":"","group":"1386e1df.83b3ae","order":1,"width":3,"height":1,"passthru":false,"mode":"text","delay":"350","topic":"","x":3150,"y":2240,"wires":[["7797971f.f78778"]]},{"id":"7797971f.f78778","type":"change","z":"705d403b.f2b8","name":"","rules":[{"t":"set","p":"newzone.ID","pt":"flow","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":3420,"y":2240,"wires":[[]]},{"id":"5b4809a9.073948","type":"ui_button","z":"705d403b.f2b8","name":"","group":"1386e1df.83b3ae","order":3,"width":1,"height":1,"passthru":false,"label":"Save","tooltip":"","color":"","bgcolor":"","icon":"","payload":"nothing","payloadType":"str","topic":"","x":2810,"y":2320,"wires":[["77413dc7.e8c9b4"]]},{"id":"77413dc7.e8c9b4","type":"function","z":"705d403b.f2b8","name":"New zone","func":"// Save new zone entered in Dashboard to global context\n// Includes form validation and duplicate check\n// and supports clearing the form when done correctly\n\n// check form was filled in properly\nif (typeof flow.get(\"newzone\") == 'undefined') {\n msg.payload = \"You didn't enter anything - zone not saved\"\n msg.error = true\n return msg\n}\n\n// assume newzone object exists (some part of form was filled)\nvar newzone = flow.get(\"newzone\")\n\n// if form not complete\nif (typeof newzone.ID == 'undefined' ||\n newzone.ID === \"\" ||\n typeof newzone.friendly_name == 'undefined' ||\n newzone.friendly_name === \"\") {\n msg.payload = \"Missing info - zone not saved\"\n msg.error = true\n return msg\n}\n\n// if we've got this far, we have a good new zone to add\n// create global zones object if it's not there\nif (typeof global.get(\"home.light.config.zones\") == 'undefined') {\n global.set(\"home.light.config.zones\", {})\n}\n\n// get light zones object\nvar zones = global.get(\"home.light.config.zones\")\n\n// does fixture ID exist?\n\nif (zones.hasOwnProperty(newzone.ID)) {\n msg.payload = \"oops, ID already exists\"\n msg.error = true\n return msg\n} else {\n d = new Date().toLocaleDateString(); t = new Date().toLocaleTimeString();\n zones[newzone.ID] = {\n \"friendly_name\": newzone.friendly_name,\n \"created_date\": d,\n \"created_time\": t\n }\n \n global.set(\"home.light.config.zones\",zones)\n msg.payload = \"Success - added zone\"\n msg.error = false\n // now remove item from flow context, otherwise our\n // validation tests above will always pass\n flow.set(\"newzone\", undefined);\n \n return msg\n}","outputs":1,"noerr":0,"x":2940,"y":2320,"wires":[["58c8c589.5d48dc","549ddba1.fb5e44"]]},{"id":"58c8c589.5d48dc","type":"ui_template","z":"705d403b.f2b8","group":"1386e1df.83b3ae","name":"New zone validation","order":4,"width":"8","height":1,"format":"{{msg.payload}}","storeOutMessages":true,"fwdInMessages":false,"templateScope":"local","x":3400,"y":2320,"wires":[[]]},{"id":"dcf2a212.18aa1","type":"delay","z":"705d403b.f2b8","name":"","pauseType":"delay","timeout":"5","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":3220,"y":2360,"wires":[["58c8c589.5d48dc"]]},{"id":"549ddba1.fb5e44","type":"function","z":"705d403b.f2b8","name":"Success","func":"// Save new zone: clear form & result if successful\n\nif (!msg.error) {\n msg.payload = \"\"\n return msg;\n}\n","outputs":1,"noerr":0,"x":3060,"y":2360,"wires":[["dcf2a212.18aa1","4714f8ee.5b3398","8f98479c.d386a8","ddc87a2a.8ae778"]]},{"id":"8f98479c.d386a8","type":"change","z":"705d403b.f2b8","name":"Get","rules":[{"t":"set","p":"zones","pt":"msg","to":"home.light.config.zones","tot":"global"}],"action":"","property":"","from":"","to":"","reg":false,"x":3210,"y":2420,"wires":[["8a402bb0.963de8"]]},{"id":"8a402bb0.963de8","type":"ui_template","z":"705d403b.f2b8","group":"1386e1df.83b3ae","name":"Zones List","order":5,"width":"12","height":"8","format":"<h3>Register of Light Zones</h3>\n\n<table>\n <tr style=\"font-weight:800; background-color:rgba(20,20,20,.6);\">\n <td>ID</td>\n <td>Name</td>\n <td>Added</td>\n <td>Delete*</td>\n </tr>\n <tr ng-repeat=\"(key, value) in msg.zones\">\n <td>{{key}}</td>\n <td>{{value.friendly_name}}</td>\n <td style=\"color:#888;\">{{value.created_time}} {{value.created_date}}</td>\n <td><md-button ng-click=\"send({action: 'delete', topic: key})\">Del</md-button></td>\n </tr>\n</table>","storeOutMessages":false,"fwdInMessages":false,"templateScope":"local","x":3350,"y":2420,"wires":[["ab08a35d.f692d"]]},{"id":"ab08a35d.f692d","type":"function","z":"705d403b.f2b8","name":"Set","func":"// get object from memory\n// NB we are using \"bracket notation\" just in case the ID\n// (i.e. the object name, represented here as msg.topic)\n// had a space\n\nif (msg.action == \"delete\") {\n obj = global.get(\"home.light.config.zones\")\n delete obj[msg.topic]\n global.set(\"home.light.config.zones\", obj)\n}\n\nreturn msg;\n","outputs":1,"noerr":0,"x":3490,"y":2420,"wires":[["8f98479c.d386a8"]]},{"id":"1dabe67a.a9504a","type":"inject","z":"705d403b.f2b8","name":"Update view","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":3030,"y":2420,"wires":[["8f98479c.d386a8"]]},{"id":"ddc87a2a.8ae778","type":"ui_text_input","z":"705d403b.f2b8","name":"","label":"Friendly Name","tooltip":"","group":"1386e1df.83b3ae","order":2,"width":3,"height":1,"passthru":true,"mode":"text","delay":"1000","topic":"","x":3180,"y":2260,"wires":[["9c619228.80fde"]]},{"id":"9c619228.80fde","type":"change","z":"705d403b.f2b8","name":"","rules":[{"t":"set","p":"newzone.friendly_name","pt":"flow","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":3450,"y":2260,"wires":[[]]},{"id":"aed3ce62.0a993","type":"change","z":"705d403b.f2b8","name":"","rules":[{"t":"set","p":"new_fixture_zone_mapping.fixtureID","pt":"flow","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":3490,"y":2580,"wires":[[]]},{"id":"c45f942b.25fb78","type":"ui_button","z":"705d403b.f2b8","name":"","group":"1598a8ff.884b77","order":3,"width":1,"height":1,"passthru":false,"label":"Assign","tooltip":"","color":"","bgcolor":"","icon":"","payload":"nothing","payloadType":"str","topic":"","x":2710,"y":2660,"wires":[["bcc049ac.db8cd8"]]},{"id":"bcc049ac.db8cd8","type":"function","z":"705d403b.f2b8","name":"New fixture_zone_mapping","func":"// NEEDS RE-WRITING FOR FIXTURE TO ZONE ASSIGNMENT\n// ID will be automatically generated as per:\n// zoneID_fixtureID\n// if such an ID exists, then message: already exists in zone\n// Don't forget to update code for deleting a zone and\n// a fixture. Each should check for corresponding entries\n// in the mappings object and remove these entries\n\n\n// Save new zone entered in Dashboard to global context\n// Includes form validation and duplicate check\n// and supports clearing the form when done correctly\n\n// check form was filled in properly\nif (typeof flow.get(\"new_fixture_zone_mapping\") == 'undefined') {\n msg.payload = \"You didn't enter anything - fixture not assigned\"\n msg.error = true\n return msg\n}\n\n// assume newzone object exists (some part of form was filled)\nvar new_fixture_zone_mapping = flow.get(\"new_fixture_zone_mapping\")\n\n// if form not complete\nif (typeof new_fixture_zone_mapping.fixtureID == 'undefined' ||\n new_fixture_zone_mapping.fixtureID === \"\" ||\n typeof new_fixture_zone_mapping.zoneID == 'undefined' ||\n new_fixture_zone_mapping.zoneID === \"\") {\n msg.payload = \"Missing info - fixture not assigned\"\n msg.error = true\n return msg\n}\n\n// if we've got this far, we have a good assignment to create\n// create global assignment object if it's not there\nif (typeof global.get(\"home.light.config.fixture_zone_mappings\") == 'undefined') {\n global.set(\"home.light.config.fixture_zone_mappings\", {})\n}\n\n// get light fixture zone mappings object\nvar fixture_zone_mappings = global.get(\"home.light.config.fixture_zone_mappings\")\n\n// does mapping ID exist? (it will look like this: zoneID_fixtureID)\nvar newID = new_fixture_zone_mapping.zoneID + \"_\" + new_fixture_zone_mapping.fixtureID\n\nif (fixture_zone_mappings.hasOwnProperty(newID)) {\n msg.payload = \"oops, fixture already added to zone\"\n msg.error = true\n return msg\n} else {\n d = new Date().toLocaleDateString(); t = new Date().toLocaleTimeString();\n fixture_zone_mappings[newID] = {\n \"zoneID\": new_fixture_zone_mapping.zoneID, // technically not necessary\n \"fixtureID\": new_fixture_zone_mapping.fixtureID, // technically not necessary\n \"created_date\": d,\n \"created_time\": t\n }\n \n global.set(\"home.light.config.fixture_zone_mappings\",fixture_zone_mappings)\n msg.payload = \"Success - assigned fixture to zone\"\n msg.error = false\n // now remove item from flow context, otherwise our\n // validation tests above will always pass\n flow.set(\"new_fixture_zone_mapping\", undefined);\n \n return msg\n}","outputs":1,"noerr":0,"x":2900,"y":2660,"wires":[["4bafda67.14c724","c903cb21.a24cd8"]]},{"id":"4bafda67.14c724","type":"ui_template","z":"705d403b.f2b8","group":"1598a8ff.884b77","name":"New fixture-zone validation","order":4,"width":"8","height":1,"format":"{{msg.payload}}","storeOutMessages":true,"fwdInMessages":false,"templateScope":"local","x":3420,"y":2660,"wires":[[]]},{"id":"121995cb.dae1aa","type":"delay","z":"705d403b.f2b8","name":"","pauseType":"delay","timeout":"5","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":3220,"y":2700,"wires":[["4bafda67.14c724"]]},{"id":"c903cb21.a24cd8","type":"function","z":"705d403b.f2b8","name":"Success","func":"// Save new zone: clear form & result if successful\n\nif (!msg.error) {\n msg.payload = \"\"\n return msg;\n}\n","outputs":1,"noerr":0,"x":3060,"y":2700,"wires":[["121995cb.dae1aa","a3100af0.716338","b7e6e6f8.691188","a944297e.5ee6f8"]]},{"id":"a3100af0.716338","type":"change","z":"705d403b.f2b8","name":"Get","rules":[{"t":"set","p":"fixture_zone_mappings","pt":"msg","to":"home.light.config.fixture_zone_mappings","tot":"global"}],"action":"","property":"","from":"","to":"","reg":false,"x":3210,"y":2760,"wires":[["4e545849.410518"]]},{"id":"4e545849.410518","type":"ui_template","z":"705d403b.f2b8","group":"1598a8ff.884b77","name":"Fixture to Zone Mappings List","order":5,"width":"12","height":"8","format":"<h3>Register of Fixture to Zone Mappings</h3>\n\n<table>\n <tr style=\"font-weight:800; background-color:rgba(20,20,20,.6);\">\n <td>ID</td>\n <td>Zone</td>\n <td>Fixture</td>\n <td>Added</td>\n <td>Delete*</td>\n </tr>\n <tr ng-repeat=\"(key, value) in msg.fixture_zone_mappings\">\n <td>{{key}}</td>\n <td>{{value.zoneID}}</td>\n <td>{{value.fixtureID}}</td>\n <td style=\"color:#888;\">{{value.created_time}} {{value.created_date}}</td>\n <td><md-button ng-click=\"send({action: 'delete', topic: key})\">Del</md-button></td>\n </tr>\n</table>","storeOutMessages":false,"fwdInMessages":false,"templateScope":"local","x":3410,"y":2760,"wires":[["c0c7b85b.4670e8"]]},{"id":"c0c7b85b.4670e8","type":"function","z":"705d403b.f2b8","name":"Set","func":"// get object from memory\n// NB we are using \"bracket notation\" just in case the ID\n// (i.e. the object name, represented here as msg.topic)\n// had a space\n\nif (msg.action == \"delete\") {\n obj = global.get(\"home.light.config.fixture_zone_mappings\")\n delete obj[msg.topic]\n global.set(\"home.light.config.fixture_zone_mappings\", obj)\n}\n\nreturn msg;\n","outputs":1,"noerr":0,"x":3610,"y":2760,"wires":[["a3100af0.716338"]]},{"id":"863b3a11.caffb8","type":"inject","z":"705d403b.f2b8","name":"Update view","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":3030,"y":2760,"wires":[["a3100af0.716338"]]},{"id":"66275f51.ec58b","type":"change","z":"705d403b.f2b8","name":"","rules":[{"t":"set","p":"new_fixture_zone_mapping.zoneID","pt":"flow","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":3490,"y":2600,"wires":[[]]},{"id":"551ae558.ece26c","type":"ui_dropdown","z":"705d403b.f2b8","name":"Fixture Select","label":"","tooltip":"","place":"Select Fixture","group":"1598a8ff.884b77","order":1,"width":"3","height":"1","passthru":true,"options":[],"payload":"","topic":"","x":3220,"y":2580,"wires":[["aed3ce62.0a993"]]},{"id":"b7e6e6f8.691188","type":"function","z":"705d403b.f2b8","name":"","func":"msg.options = Object.keys((global.get(\"home.light.config.fixtures\")))\nreturn msg;","outputs":1,"noerr":0,"x":3050,"y":2580,"wires":[["551ae558.ece26c","cb5e87f1.d11458"]]},{"id":"8a237566.217618","type":"inject","z":"705d403b.f2b8","name":"manually refresh select lists","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":2760,"y":2580,"wires":[["b7e6e6f8.691188","a944297e.5ee6f8"]]},{"id":"cb5e87f1.d11458","type":"debug","z":"705d403b.f2b8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","x":3180,"y":2520,"wires":[]},{"id":"3ebfbf6e.973d6","type":"ui_dropdown","z":"705d403b.f2b8","name":"Zone Select","label":"","tooltip":"","place":"Select Zone","group":"1598a8ff.884b77","order":2,"width":"3","height":"1","passthru":true,"options":[],"payload":"","topic":"","x":3210,"y":2600,"wires":[["66275f51.ec58b"]]},{"id":"a944297e.5ee6f8","type":"function","z":"705d403b.f2b8","name":"","func":"msg.options = Object.keys((global.get(\"home.light.config.zones\")))\nreturn msg;","outputs":1,"noerr":0,"x":3050,"y":2600,"wires":[["3ebfbf6e.973d6"]]},{"id":"1a37e545.435d2b","type":"ui_group","z":"","name":"New Fixture","tab":"869c06ba.dfea88","order":1,"disp":true,"width":"12","collapse":false},{"id":"1386e1df.83b3ae","type":"ui_group","z":"","name":"New Zone","tab":"869c06ba.dfea88","order":2,"disp":true,"width":"12","collapse":false},{"id":"1598a8ff.884b77","type":"ui_group","z":"","name":"Fixture to Zone Assignment","tab":"869c06ba.dfea88","order":3,"disp":true,"width":"12","collapse":false},{"id":"869c06ba.dfea88","type":"ui_tab","z":"","name":"Manage Presets","icon":"dashboard","disabled":false,"hidden":false}]