The conversation began here https://discourse.nodered.org/t/led-icon-changed/656/16?u=ozpos
I have made significant progress but still have a couple of problems.

[
    {
        "id": "77b9551a682cccf9",
        "type": "tab",
        "label": "Flow 3",
        "disabled": false,
        "info": ""
    },
    {
        "id": "7e1c80b7bacaa1eb",
        "type": "debug",
        "z": "77b9551a682cccf9",
        "name": "",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 530,
        "y": 160,
        "wires": []
    },
    {
        "id": "edb3ddb6cd354130",
        "type": "ui_template",
        "z": "77b9551a682cccf9",
        "group": "879c5e97.07fff",
        "name": "LED Array",
        "order": 13,
        "width": "0",
        "height": "0",
        "format": "<script>\n(function($scope) {\n    // Inspired by h t t p s://flows.nodered.org/user/Hugobox\n    let led_count = 32\n    let label = \"GPIO\"\n    let initialised = false\n    var bargraph = new Array(led_count).fill(\"#000000\")\n\n    //From @Hotnip: cause a small delay while things load\n    //ideally this would be an init event or on all parts of document loaded\n    //(may not be necessary!)\n    setTimeout(function() {\n        //debugger\n        $scope.init();\n    },100);\n\n    /**\n    * Inform controller of browser refresh or nr server restart or deploy.\n    */\n    $scope.init = function () {\n        //debugger\n        console.log(\"$scope.init called.\");\n        \n        if(initialised == false){\n            //debugger\n            console.log(\"Sending _init_ msg\");\n            $scope.send({payload: bargraph, topic: \"_init_\"});\n        }\n    };\n\n    $scope.$watch('msg', function() {\n        \n        if ($scope.msg){\n            if ($scope.msg.hasOwnProperty('payload') &&\n                typeof $scope.msg.payload == \"object\" &&\n                Array.isArray( $scope.msg.payload ) &&\n                $scope.msg.payload.length <= led_count && \n                $scope.msg.payload.length > 0 ){\n\n                console.log(\"msg.payload[0]=\" + String($scope.msg.payload[0]) + \" typeof \" + typeof $scope.msg.payload + \" isArray \" + Array.isArray($scope.msg.payload) + \" length \" + $scope.msg.payload.length + \" typeof \" + typeof $scope.msg.payload[0])\n                $scope.msg.label = label\n                for(let i=0; i<led_count && i<$scope.msg.payload.length; i++){\n                    if ( typeof $scope.msg.payload[i] == 'string'){\n                        bargraph[i]=$scope.msg.payload[i]\n                    }\n                }\n                $scope.msg.bargraph = bargraph.reverse()\n                $scope.msg.bargraph = bargraph.reverse()\n            }else if (typeof $scope.msg.payload !== \"number\"){\n                $scope.msg = {\"bargraph\":[...bargraph], \"payload\": 0, \"label\":label}\n            }   \n        }else{\n            $scope.msg = {\"bargraph\":[...bargraph], \"payload\": 0, \"label\":label}\n        }\n    });\n})(scope);\n</script>\n\n<style>\n.bargraph {\n    float: right;\n    padding: 3px;\n    width: 3px;\n    height: 10px;\n    margin: 4px 2px 8px 0px;\n    border-radius: 0%;\n}\n</style>\n\n<div>{{msg.label}}\n<div>\n    <span ng-repeat=\"led in msg.payload track by $index\">\n        <span class=\"bargraph\" style=\"background-color: {{msg.payload[$index]}}; box-shadow: black 0 -1px 1px 0px, inset black  0 -1px 4px, {{msg.payload[$index]}} 0 3px 15px;\"></span>\n    </span>\n</div>\n</div>\n",
        "storeOutMessages": false,
        "fwdInMessages": false,
        "resendOnRefresh": false,
        "templateScope": "local",
        "x": 370,
        "y": 160,
        "wires": [
            [
                "7e1c80b7bacaa1eb",
                "52ab717f225a0b77"
            ]
        ]
    },
    {
        "id": "29bb58bf97a78ce9",
        "type": "inject",
        "z": "77b9551a682cccf9",
        "name": "32 colours",
        "props": [
            {
                "p": "payload"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "[\"#0000ff\",\"#ff0000\",\"#00ff00\",\"#0000ff\",\"#ff0000\",\"#00ff00\",\"#0000ff\",\"#ff0000\",\"#00ff00\",\"#0000ff\",\"#ff0000\",\"#00ff00\",\"#0000ff\",\"#ff0000\",\"#00ff00\", \"#0000ff\",\"#ff0000\",\"#00ff00\",\"#0000ff\",\"#ff0000\",\"#00ff00\",\"#0000ff\",\"#ff0000\",\"#00ff00\",\"#0000ff\",\"#ff0000\",\"#00ff00\",\"#0000ff\",\"#ff0000\",\"#00ff00\",\"#0000ff\",\"#ff0000\"]",
        "payloadType": "jsonata",
        "x": 180,
        "y": 120,
        "wires": [
            [
                "edb3ddb6cd354130",
                "09efd2d2113522b6"
            ]
        ]
    },
    {
        "id": "81e0d7df3c97b312",
        "type": "inject",
        "z": "77b9551a682cccf9",
        "name": "3 colours",
        "props": [
            {
                "p": "payload"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "[\"#ff0000\",\"#00ff00\",\"#0000ff\"]",
        "payloadType": "jsonata",
        "x": 180,
        "y": 80,
        "wires": [
            [
                "edb3ddb6cd354130",
                "09efd2d2113522b6"
            ]
        ]
    },
    {
        "id": "c3c3ef63ebd223ce",
        "type": "inject",
        "z": "77b9551a682cccf9",
        "name": "1 colour",
        "props": [
            {
                "p": "payload"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "[\"#ffff00\"]",
        "payloadType": "jsonata",
        "x": 190,
        "y": 40,
        "wires": [
            [
                "edb3ddb6cd354130"
            ]
        ]
    },
    {
        "id": "9d69baab914b9193",
        "type": "inject",
        "z": "77b9551a682cccf9",
        "name": "empty array",
        "props": [
            {
                "p": "payload"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "[]",
        "payloadType": "jsonata",
        "x": 390,
        "y": 40,
        "wires": [
            [
                "edb3ddb6cd354130"
            ]
        ]
    },
    {
        "id": "af5b84708f88cb03",
        "type": "comment",
        "z": "77b9551a682cccf9",
        "name": "sending empty array deleted the leds.",
        "info": "",
        "x": 430,
        "y": 280,
        "wires": []
    },
    {
        "id": "52ab717f225a0b77",
        "type": "function",
        "z": "77b9551a682cccf9",
        "name": "gpio",
        "func": "let gpio = flow.get(node.name);\nlet refresh_ui = false;\nlet cmd = String(msg.topic).split(\" \");\n\nswitch (cmd[0]){\n    case \"_init_\":\n        if (JSON.stringify(gpio.payload) !== JSON.stringify(msg.payload)) {\n            refresh_ui = true;\n        }\n        break;\n\n    case \"w\":\n        let led = parseInt(msg.payload);\n        if (led >= 0 && led < 32){\n            gpio.payload[led] = cmd[1];\n            flow.set(node.name, gpio);\n            refresh_ui = true;\n        }\n\n}\nif ( refresh_ui ){\n    msg.topic = \"_init_\";\n    msg.payload = gpio.payload;\n    return msg;\n}\n",
        "outputs": 1,
        "noerr": 0,
        "initialize": "// Code added here will be run once\n// whenever the node is started.\nlet gpio = flow.get(node.name);\nif ( gpio === undefined){\n    gpio = {\n        payload: [\"#0000ff\", \"#ff0000\", \"#00ff00\", \"#0000ff\", \"#ff0000\", \"#00ff00\", \"#0000ff\", \"#ff0000\", \"#00ff00\", \"#0000ff\", \"#ff0000\", \"#00ff00\", \"#0000ff\", \"#ff0000\", \"#00ff00\", \"#0000ff\", \"#ff0000\", \"#00ff00\", \"#0000ff\", \"#ff0000\", \"#00ff00\", \"#0000ff\", \"#ff0000\", \"#00ff00\", \"#0000ff\", \"#ff0000\", \"#00ff00\", \"#0000ff\", \"#ff0000\", \"#00ff00\", \"#0000ff\", \"#ff0000\"],\n        topic: \"_init_\"\n    }\n    flow.set(node.name, gpio);\n}\n",
        "finalize": "",
        "libs": [],
        "x": 370,
        "y": 200,
        "wires": [
            [
                "edb3ddb6cd354130",
                "d81a5f772e875be5"
            ]
        ]
    },
    {
        "id": "ff467ed83e025341",
        "type": "inject",
        "z": "77b9551a682cccf9",
        "name": "w #ffff00 to 2",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "w #ffff00",
        "payload": "2",
        "payloadType": "num",
        "x": 170,
        "y": 180,
        "wires": [
            [
                "52ab717f225a0b77"
            ]
        ]
    },
    {
        "id": "d81a5f772e875be5",
        "type": "debug",
        "z": "77b9551a682cccf9",
        "name": "",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 530,
        "y": 200,
        "wires": []
    },
    {
        "id": "11f35c798c27084f",
        "type": "comment",
        "z": "77b9551a682cccf9",
        "name": "sending array length of 1 shows only one led leds.",
        "info": "",
        "x": 420,
        "y": 320,
        "wires": []
    },
    {
        "id": "95f023d4c0c7aa7b",
        "type": "inject",
        "z": "77b9551a682cccf9",
        "name": "w #ffff00 to 31",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "w #ffff00",
        "payload": "31",
        "payloadType": "num",
        "x": 170,
        "y": 220,
        "wires": [
            [
                "52ab717f225a0b77"
            ]
        ]
    },
    {
        "id": "20b47688438693c9",
        "type": "comment",
        "z": "77b9551a682cccf9",
        "name": "Outstanding Problems",
        "info": "",
        "x": 450,
        "y": 240,
        "wires": []
    },
    {
        "id": "09efd2d2113522b6",
        "type": "ui_template",
        "z": "77b9551a682cccf9",
        "group": "879c5e97.07fff",
        "name": "LED Array 2",
        "order": 13,
        "width": "0",
        "height": "0",
        "format": "<script>\n(function($scope) {\n    // Inspired by h t t p s://flows.nodered.org/user/Hugobox\n    let led_count = 32\n    let label = \"GPIO\"\n    let initialised = false\n    var bargraph = new Array(led_count).fill(\"#000000\")\n\n    //From @Hotnip: cause a small delay while things load\n    //ideally this would be an init event or on all parts of document loaded\n    //(may not be necessary!)\n    setTimeout(function() {\n        //debugger\n        $scope.init();\n    },100);\n\n    /**\n    * Inform controller of browser refresh or nr server restart or deploy.\n    */\n    $scope.init = function () {\n        //debugger\n        console.log(\"$scope.init called.\");\n        \n        if(initialised == false){\n            //debugger\n            console.log(\"Sending _init_ msg\");\n            $scope.send({payload: bargraph, topic: \"_init_\"});\n        }\n    };\n\n    $scope.$watch('msg', function() {\n        \n        if ($scope.msg){\n            if ($scope.msg.hasOwnProperty('payload') &&\n                typeof $scope.msg.payload == \"object\" &&\n                Array.isArray( $scope.msg.payload ) &&\n                $scope.msg.payload.length <= led_count && \n                $scope.msg.payload.length > 0 ){\n\n                console.log(\"msg.payload[0]=\" + String($scope.msg.payload[0]) + \" typeof \" + typeof $scope.msg.payload + \" isArray \" + Array.isArray($scope.msg.payload) + \" length \" + $scope.msg.payload.length + \" typeof \" + typeof $scope.msg.payload[0])\n                $scope.msg.label = label\n                for(let i=0; i<led_count && i<$scope.msg.payload.length; i++){\n                    if ( typeof $scope.msg.payload[i] == 'string'){\n                        bargraph[i]=$scope.msg.payload[i]\n                    }\n                }\n                $scope.msg.bargraph = bargraph.reverse()\n                $scope.msg.bargraph = bargraph.reverse()\n            }else if (typeof $scope.msg.payload !== \"number\"){\n                $scope.msg = {\"bargraph\":[...bargraph], \"payload\": 0, \"label\":label}\n            }   \n        }else{\n            $scope.msg = {\"bargraph\":[...bargraph], \"payload\": 0, \"label\":label}\n        }\n    });\n})(scope);\n</script>\n\n<style>\n.bargraph {\n    float: right;\n    padding: 3px;\n    width: 3px;\n    height: 10px;\n    margin: 4px 2px 8px 0px;\n    border-radius: 0%;\n}\n</style>\n\n<div>{{msg.label}}\n<div>\n    <span ng-repeat=\"led in msg.payload track by $index\">\n        <span class=\"bargraph\" style=\"background-color: {{msg.payload[$index]}}; box-shadow: black 0 -1px 1px 0px, inset black  0 -1px 4px, {{msg.payload[$index]}} 0 3px 15px;\"></span>\n    </span>\n</div>\n</div>\n",
        "storeOutMessages": false,
        "fwdInMessages": false,
        "resendOnRefresh": false,
        "templateScope": "local",
        "x": 590,
        "y": 100,
        "wires": [
            [
                "a0343d6543eb0cf8",
                "c8a91fd27e9606f1"
            ]
        ]
    },
    {
        "id": "73a3d4698afcfc82",
        "type": "inject",
        "z": "77b9551a682cccf9",
        "name": "w #ffff00 to 0",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "w #ffff00",
        "payload": "0",
        "payloadType": "num",
        "x": 610,
        "y": 20,
        "wires": [
            [
                "09efd2d2113522b6"
            ]
        ]
    },
    {
        "id": "aa74d58c7c373f63",
        "type": "inject",
        "z": "77b9551a682cccf9",
        "name": "w #ffff00 to 30",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "w #ffff00",
        "payload": "30",
        "payloadType": "num",
        "x": 610,
        "y": 60,
        "wires": [
            [
                "09efd2d2113522b6"
            ]
        ]
    },
    {
        "id": "a0343d6543eb0cf8",
        "type": "debug",
        "z": "77b9551a682cccf9",
        "name": "",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 750,
        "y": 140,
        "wires": []
    },
    {
        "id": "c8a91fd27e9606f1",
        "type": "function",
        "z": "77b9551a682cccf9",
        "name": "gpio2",
        "func": "let gpio = flow.get(node.name);\nlet refresh_ui = false;\nlet cmd = String(msg.topic).split(\" \");\n\nswitch (cmd[0]){\n    case \"_init_\":\n        if (JSON.stringify(gpio.payload) !== JSON.stringify(msg.payload)) {\n            refresh_ui = true;\n        }\n        break;\n\n    case \"w\":\n        let led = parseInt(msg.payload);\n        if (led >= 0 && led < 32){\n            gpio.payload[led] = cmd[1];\n            flow.set(node.name, gpio);\n            refresh_ui = true;\n        }\n\n}\nif ( refresh_ui ){\n    msg.topic = \"_init_\";\n    msg.payload = gpio.payload;\n    return msg;\n}\n",
        "outputs": 1,
        "noerr": 0,
        "initialize": "// Code added here will be run once\n// whenever the node is started.\nlet gpio = flow.get(node.name);\nif ( gpio === undefined){\n    gpio = {\n        payload: [\"#0000ff\", \"#ff0000\", \"#00ff00\", \"#0000ff\", \"#ff0000\", \"#00ff00\", \"#0000ff\", \"#ff0000\", \"#00ff00\", \"#0000ff\", \"#ff0000\", \"#00ff00\", \"#0000ff\", \"#ff0000\", \"#00ff00\", \"#0000ff\", \"#ff0000\", \"#00ff00\", \"#0000ff\", \"#ff0000\", \"#00ff00\", \"#0000ff\", \"#ff0000\", \"#00ff00\", \"#0000ff\", \"#ff0000\", \"#00ff00\", \"#0000ff\", \"#ff0000\", \"#00ff00\", \"#0000ff\", \"#ff0000\"],\n        topic: \"_init_\"\n    }\n    flow.set(node.name, gpio);\n}\n",
        "finalize": "",
        "libs": [],
        "x": 690,
        "y": 200,
        "wires": [
            [
                "09efd2d2113522b6"
            ]
        ]
    },
    {
        "id": "7c2c748c90daf304",
        "type": "comment",
        "z": "77b9551a682cccf9",
        "name": "How to use more than one led array template ?",
        "info": "",
        "x": 420,
        "y": 360,
        "wires": []
    },
    {
        "id": "879c5e97.07fff",
        "type": "ui_group",
        "name": "n:14.17.5 r:2.0.5 d:20.30.0",
        "tab": "105c5e70.b644f2",
        "order": 4,
        "disp": true,
        "width": "8",
        "collapse": true
    },
    {
        "id": "105c5e70.b644f2",
        "type": "ui_tab",
        "name": "dashboard template",
        "icon": "dashboard",
        "order": 1,
        "disabled": false,
        "hidden": false
    }
]
The list of problems I now have are:
- get http://127.0.0.1:1893/ui/loading.html 404 (Not Found)
- I cannot seem to protect the template from spurious array sizes in msg.payload.
- What has to change to allow multiple independent led array templates on the same flow.
 Many thanks in advance.





 


 
 
 
 








 Need coffee
   Need coffee 










