What is the BEST way to flash a Dashboard LED?

This is a little flow to flash an led on the dashboard but it uses 5 nodes and one of them is a Function.

I wonder if there is a more elegant low-code solution using core nodes, and fewer of them?

[{"id":"aaa27ee4852b402b","type":"tab","label":"Flash LED","disabled":false,"info":"","env":[]},{"id":"2f0b6dfa2edb0d4d","type":"change","z":"aaa27ee4852b402b","name":"cnt = 0","rules":[{"t":"set","p":"thismany","pt":"msg","to":"payload","tot":"msg"},{"t":"set","p":"sofar","pt":"msg","to":"0","tot":"num"},{"t":"set","p":"delay","pt":"msg","to":"topic","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":370,"y":100,"wires":[["418d143f6e3826f7"]]},{"id":"418d143f6e3826f7","type":"trigger","z":"aaa27ee4852b402b","name":"400ms","op1":"false","op2":"true","op1type":"bool","op2type":"bool","duration":"400","extend":false,"overrideDelay":true,"units":"ms","reset":"","bytopic":"all","topic":"topic","outputs":1,"x":510,"y":100,"wires":[["c94f6afeec0f2e6f"]]},{"id":"c94f6afeec0f2e6f","type":"function","z":"aaa27ee4852b402b","name":"cnt ++","func":"if (msg.sofar ++ < msg.thismany) {\n    return(msg);\n}","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":630,"y":100,"wires":[["d725f02547473fe0","0c9cf4f3bdde4463"]]},{"id":"d725f02547473fe0","type":"ui_led","z":"aaa27ee4852b402b","order":2,"group":"360b409060b23c5b","width":"0","height":"0","label":"","labelPlacement":"left","labelAlignment":"left","colorForValue":[{"color":"#ff0000","value":"false","valueType":"bool"},{"color":"#000000","value":"true","valueType":"bool"}],"allowColorForValueInMessage":false,"shape":"circle","showGlow":false,"name":"","x":790,"y":100,"wires":[]},{"id":"2928440d2d7837b3","type":"ui_button","z":"aaa27ee4852b402b","name":"","group":"360b409060b23c5b","order":3,"width":"2","height":"1","passthru":false,"label":"Flash 1","tooltip":"","color":"","bgcolor":"","className":"","icon":"","payload":"1","payloadType":"str","topic":"topic","topicType":"msg","x":200,"y":60,"wires":[["2f0b6dfa2edb0d4d"]]},{"id":"d36145aea2c0d16a","type":"ui_button","z":"aaa27ee4852b402b","name":"","group":"360b409060b23c5b","order":3,"width":"2","height":"1","passthru":false,"label":"Flash 3","tooltip":"","color":"","bgcolor":"","className":"","icon":"","payload":"3","payloadType":"num","topic":"topic","topicType":"msg","x":200,"y":100,"wires":[["2f0b6dfa2edb0d4d"]]},{"id":"8fc2f37d8b8d1eba","type":"delay","z":"aaa27ee4852b402b","name":"400ms","pauseType":"delay","timeout":"400","timeoutUnits":"milliseconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"allowrate":false,"outputs":1,"x":630,"y":140,"wires":[["418d143f6e3826f7"]]},{"id":"838edfe2e54088bc","type":"ui_button","z":"aaa27ee4852b402b","name":"","group":"360b409060b23c5b","order":3,"width":"2","height":"1","passthru":false,"label":"Long Flash 5","tooltip":"","color":"","bgcolor":"","className":"","icon":"","payload":"5","payloadType":"num","topic":"1000","topicType":"str","x":210,"y":140,"wires":[["2f0b6dfa2edb0d4d"]]},{"id":"0c9cf4f3bdde4463","type":"switch","z":"aaa27ee4852b402b","name":"","property":"payload","propertyType":"msg","rules":[{"t":"true"}],"checkall":"true","repair":false,"outputs":1,"x":510,"y":140,"wires":[["8fc2f37d8b8d1eba"]]},{"id":"01d97a3db02a8127","type":"ui_button","z":"aaa27ee4852b402b","name":"","group":"360b409060b23c5b","order":3,"width":"2","height":"1","passthru":false,"label":"Short Flash 5","tooltip":"","color":"","bgcolor":"","className":"","icon":"","payload":"5","payloadType":"num","topic":"200","topicType":"str","x":210,"y":180,"wires":[["2f0b6dfa2edb0d4d"]]},{"id":"360b409060b23c5b","type":"ui_group","name":"Demo","tab":"0c531246b2a3f00e","order":1,"disp":true,"width":"6","collapse":false,"className":""},{"id":"0c531246b2a3f00e","type":"ui_tab","name":"Demo","icon":"dashboard","disabled":false,"hidden":false}]

Create the various patterns as CSS classes in a head template & simply set the class accordingly.

Here is a decent CSS article on flashing dots: Blinking dots: a quick intro to CSS animation - DEV Community 👩‍💻👨‍💻

2 Likes

CSS - good thought. And with fade-in and out too. I could use that to make a lighthouse simulator with flashing, occulting, etc. Lighthouse flash codes

But - I don't think it counts as using core nodes? I know nodered-contrib-ui-led isn't a core node either.

If you are going to use a function then you only need 1 node.
Here are 4 slightly different approaches.

[{"id":"3fb67766.cbb2c8","type":"inject","z":"aaa27ee4852b402b","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"200","payload":"1010101010","payloadType":"str","x":360,"y":280,"wires":[["dd89688f.c7b02"]]},{"id":"dd89688f.c7b02","type":"split","z":"aaa27ee4852b402b","name":"","splt":"1","spltType":"len","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"","x":530,"y":280,"wires":[["4e9cfb27.fb1334"]]},{"id":"4e9cfb27.fb1334","type":"change","z":"aaa27ee4852b402b","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"$number($$.payload) = 1","tot":"jsonata"},{"t":"set","p":"delay","pt":"msg","to":"$number($$.topic) * ($$.parts.index)","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":690,"y":280,"wires":[["a94a5dce.fac198"]]},{"id":"a94a5dce.fac198","type":"delay","z":"aaa27ee4852b402b","name":"","pauseType":"delayv","timeout":"400","timeoutUnits":"milliseconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":860,"y":280,"wires":[["ea40d8ce.f01e1"]]},{"id":"ea40d8ce.f01e1","type":"debug","z":"aaa27ee4852b402b","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":1030,"y":280,"wires":[]},{"id":"951a4dc9.b79f38","type":"inject","z":"aaa27ee4852b402b","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"200","payload":"5","payloadType":"str","x":350,"y":160,"wires":[["21022e92.635112"]]},{"id":"cf054a62.3a36b8","type":"inject","z":"aaa27ee4852b402b","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"400","payload":"2","payloadType":"str","x":350,"y":120,"wires":[["f7550e34.44162"]]},{"id":"33d07144.1f6546","type":"inject","z":"aaa27ee4852b402b","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"400","payload":"3","payloadType":"str","x":370,"y":60,"wires":[["5ba6739.ccddc8c"]]},{"id":"5ba6739.ccddc8c","type":"function","z":"aaa27ee4852b402b","name":"","func":"const timeout = ((count,time) => setTimeout(() => {\n        node.send({payload: count % 2 === 0})\n        }, Number(time) * count\n    ))\nfor(let i = 0; i < Number(msg.payload) * 2; i++) {\n    timeout(i, msg.topic);\n}\nreturn null;\n","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":550,"y":60,"wires":[["91d2c758.a70948"]]},{"id":"f7550e34.44162","type":"function","z":"aaa27ee4852b402b","name":"","func":"for(let i = 0; i < Number(msg.payload) * 2; i++) {\n    node.send({payload: i % 2 === 0, delay: Number(msg.topic) * i})\n}\nreturn null;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":510,"y":120,"wires":[["5168b6da.1baa78"]]},{"id":"21022e92.635112","type":"change","z":"aaa27ee4852b402b","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"$substring(\"1010101010101010101010101010101010101010101010101010101010\", 0 , $number($$.payload) * 2)","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":520,"y":160,"wires":[["b8e91e92.ccca58"]]},{"id":"91d2c758.a70948","type":"debug","z":"aaa27ee4852b402b","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":740,"y":60,"wires":[]},{"id":"5168b6da.1baa78","type":"delay","z":"aaa27ee4852b402b","name":"","pauseType":"delayv","timeout":"400","timeoutUnits":"milliseconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":680,"y":120,"wires":[["b71e99df.9661f"]]},{"id":"b8e91e92.ccca58","type":"split","z":"aaa27ee4852b402b","name":"","splt":"1","spltType":"len","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"","x":690,"y":160,"wires":[["456471b9.2667e"]]},{"id":"456471b9.2667e","type":"change","z":"aaa27ee4852b402b","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"$number($$.payload) = 1","tot":"jsonata"},{"t":"set","p":"delay","pt":"msg","to":"$number($$.topic) * ($$.parts.index)","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":850,"y":160,"wires":[["8fc2f37d8b8d1eba"]]},{"id":"b71e99df.9661f","type":"debug","z":"aaa27ee4852b402b","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":870,"y":120,"wires":[]},{"id":"8fc2f37d8b8d1eba","type":"delay","z":"aaa27ee4852b402b","name":"","pauseType":"delayv","timeout":"400","timeoutUnits":"milliseconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":1020,"y":160,"wires":[["77f31641.d63258"]]},{"id":"77f31641.d63258","type":"debug","z":"aaa27ee4852b402b","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":1190,"y":220,"wires":[]}]

[edit] corrected function declared inside loop and other typo's

Ok, a while back I wrote some code to do something like this too.

But mine had a third constraint so when things are complete it can tidy up what the LED is doing.

It is a subflow, but open it and look at the workings.

(And forgive the unused function node in there.)

[
    {
        "id": "1bc21d7f6ceb5285",
        "type": "subflow",
        "name": "Blinker",
        "info": "2021 12 7  \nA subflow to send pulsing messages at a given interval to make something *flash*.\n\nConfigurable by setting `environment` variables in the node's settings.\n\n\n# **Output toggle node to send alternating messages at a specified frequency.**\n# \n\n## 3 things need to be set to configure this subflow\n1 - `delay`:  milliseconds between toggles (override with msg.delay)\n\n2 - `msg1`:   text message for state 1  \n\n3 - `msg2`:   text message for state 2 \n\n`msg.topic` is output and alternates between `msg1` and `msg2` to indicate which message is being sent.\n\n## Optional:\n`offmsg` - sent when it is reset.\n\nMay be handy to make sure things are *off*.\n\n## Input message\nTo start send `msg` (anything)\n\nTo stop, send `msg.reset`",
        "category": "",
        "in": [
            {
                "x": 60,
                "y": 180,
                "wires": [
                    {
                        "id": "3a95f6976aee6fd0"
                    }
                ]
            }
        ],
        "out": [
            {
                "x": 930,
                "y": 170,
                "wires": [
                    {
                        "id": "a655faf0bc379507",
                        "port": 0
                    },
                    {
                        "id": "f3b2b2634e2662cd",
                        "port": 0
                    }
                ]
            },
            {
                "x": 930,
                "y": 220,
                "wires": [
                    {
                        "id": "f3b2b2634e2662cd",
                        "port": 1
                    }
                ]
            }
        ],
        "env": [
            {
                "name": "delay",
                "type": "num",
                "value": ""
            },
            {
                "name": "msg1",
                "type": "str",
                "value": ""
            },
            {
                "name": "msg2",
                "type": "str",
                "value": ""
            },
            {
                "name": "offmsg",
                "type": "str",
                "value": ""
            }
        ],
        "meta": {
            "module": "Toggler",
            "version": "0.1.1",
            "desc": "Toggles output between `msg1` and `msg2` set in node config"
        },
        "color": "#A6BBCF",
        "icon": "node-red-contrib-bool-gate/switch.png",
        "status": {
            "x": 930,
            "y": 270,
            "wires": [
                {
                    "id": "502c279ae9a6f385",
                    "port": 0
                }
            ]
        }
    },
    {
        "id": "c53ebcc9e62a7552",
        "type": "trigger",
        "z": "1bc21d7f6ceb5285",
        "name": "resend every x",
        "op1": "1",
        "op2": "0",
        "op1type": "str",
        "op2type": "str",
        "duration": "-1",
        "extend": false,
        "overrideDelay": true,
        "units": "s",
        "reset": "",
        "bytopic": "all",
        "topic": "topic",
        "outputs": 1,
        "x": 400,
        "y": 180,
        "wires": [
            [
                "f3b2b2634e2662cd"
            ]
        ]
    },
    {
        "id": "f3b2b2634e2662cd",
        "type": "function",
        "z": "1bc21d7f6ceb5285",
        "name": "2022 02 26 *",
        "func": "delete msg.delay;\nconst msgnull = null;\nconst msgS = {};\nlet c = context.get(\"counter\") ||0;\ncontext.set(\"counter\",((c + 1) % 2));\n\nif (c == 0) {\n    //  0\n    msg.payload = env.get(\"msg1\");\n    msg.topic = \"msg1\";\n    //\n    msgS.payload =c;\n    return[msg,msgnull,msgS];\n    //\n} else if (c == 1) {\n    //  1\n    msg.payload = env.get(\"msg2\");\n    msg.topic = \"msg2\";\n    //\n    msgS.payload =c;\n    return[msgnull,msg,msgS];\n    //\n};\n\n//msgS.payload = c;\n//return [msg,msgS];\n",
        "outputs": 3,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 590,
        "y": 180,
        "wires": [
            [],
            [],
            [
                "502c279ae9a6f385"
            ]
        ],
        "outputLabels": [
            "msgA",
            "msgB",
            "which message"
        ],
        "info": "This has 2 outputs to allow message modification straight out of subflowl\n`msg.stop` is sent to only the first output"
    },
    {
        "id": "c183245c1a6a7fac",
        "type": "switch",
        "z": "1bc21d7f6ceb5285",
        "name": "is reset?",
        "property": "reset",
        "propertyType": "msg",
        "rules": [
            {
                "t": "nnull"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 1,
        "x": 380,
        "y": 270,
        "wires": [
            [
                "a9be61803cd67654",
                "6e3a0cdb44f6a994"
            ]
        ]
    },
    {
        "id": "a9be61803cd67654",
        "type": "switch",
        "z": "1bc21d7f6ceb5285",
        "name": "off msg?",
        "property": "offmsg",
        "propertyType": "env",
        "rules": [
            {
                "t": "nempty"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 1,
        "x": 580,
        "y": 230,
        "wires": [
            [
                "a655faf0bc379507"
            ]
        ]
    },
    {
        "id": "a655faf0bc379507",
        "type": "change",
        "z": "1bc21d7f6ceb5285",
        "name": "set off message",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "offmsg",
                "tot": "env"
            },
            {
                "t": "delete",
                "p": "reset",
                "pt": "msg"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 790,
        "y": 230,
        "wires": [
            []
        ]
    },
    {
        "id": "502c279ae9a6f385",
        "type": "function",
        "z": "1bc21d7f6ceb5285",
        "name": "status",
        "func": "const c = msg.payload;\n\nif (c == 0) {\n    msg.payload = ({text: \"msg1\"});\n} else if (c == 1) {\n    msg.payload = ({text: \"msg2\"});\n} else {\n    msg.payload = ({text:\"\"});\n};\n\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 760,
        "y": 270,
        "wires": [
            []
        ]
    },
    {
        "id": "6e3a0cdb44f6a994",
        "type": "change",
        "z": "1bc21d7f6ceb5285",
        "name": "reset",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "2",
                "tot": "num"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 570,
        "y": 270,
        "wires": [
            [
                "502c279ae9a6f385"
            ]
        ]
    },
    {
        "id": "3a95f6976aee6fd0",
        "type": "function",
        "z": "1bc21d7f6ceb5285",
        "name": "set msg.delay",
        "func": "msg.delay = msg.delay || env.get(\"delay\") || 1000;\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 200,
        "y": 180,
        "wires": [
            [
                "c53ebcc9e62a7552",
                "c183245c1a6a7fac"
            ]
        ]
    },
    {
        "id": "c368646caaac4dd2",
        "type": "function",
        "z": "1bc21d7f6ceb5285",
        "name": "toggle Original",
        "func": "delete msg.delay;\nconst msgB = {};\nconst msgS = {};\nlet c = context.get(\"counter\") ||0;\n\nif (c == 0) {\n    //  0\n    msg.payload = env.get(\"msg1\");\n    msg.topic = \"msg1\";\n} else if (c == 1) {\n    //  1\n    msg.payload = env.get(\"msg2\");\n    msg.topic = \"msg2\";\n};\n\nmsgS.payload = c;\nc = (c + 1) % 2;\ncontext.set(\"counter\",c);\nreturn [msg,msgS];",
        "outputs": 2,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 590,
        "y": 120,
        "wires": [
            [],
            []
        ]
    },
    {
        "id": "ac62f676a588d6f7",
        "type": "subflow:1bc21d7f6ceb5285",
        "z": "f7d630bfc1b2f259",
        "name": "",
        "x": 560,
        "y": 1020,
        "wires": [
            [],
            []
        ]
    }
]

Here is CSS flashing and all core nodes (except for node-red-dashboard of course)

chrome_jH95dPIDb1

[{"id":"2928440d2d7837b3","type":"ui_button","z":"aaa27ee4852b402b","name":"","group":"360b409060b23c5b","order":1,"width":3,"height":1,"passthru":false,"label":"Flash red 1","tooltip":"","color":"","bgcolor":"","className":"","icon":"","payload":"red on blink c1","payloadType":"str","topic":"setClass","topicType":"str","x":210,"y":60,"wires":[["808ff37d8b2460e3"]]},{"id":"d36145aea2c0d16a","type":"ui_button","z":"aaa27ee4852b402b","name":"","group":"360b409060b23c5b","order":2,"width":3,"height":1,"passthru":false,"label":"Flash green 3","tooltip":"","color":"","bgcolor":"","className":"","icon":"","payload":"green on blink c3","payloadType":"str","topic":"setClass","topicType":"str","x":200,"y":100,"wires":[["808ff37d8b2460e3"]]},{"id":"838edfe2e54088bc","type":"ui_button","z":"aaa27ee4852b402b","name":"","group":"360b409060b23c5b","order":3,"width":3,"height":1,"passthru":false,"label":"Long Flash 5","tooltip":"","color":"","bgcolor":"","className":"","icon":"","payload":"orange on blink c5 slow","payloadType":"str","topic":"setClass","topicType":"str","x":210,"y":140,"wires":[["808ff37d8b2460e3"]]},{"id":"01d97a3db02a8127","type":"ui_button","z":"aaa27ee4852b402b","name":"","group":"360b409060b23c5b","order":4,"width":3,"height":1,"passthru":false,"label":"Short Flash 5","tooltip":"","color":"","bgcolor":"","className":"","icon":"","payload":"blue on blink c5 fast","payloadType":"str","topic":"setClass","topicType":"str","x":210,"y":180,"wires":[["808ff37d8b2460e3"]]},{"id":"808ff37d8b2460e3","type":"ui_template","z":"aaa27ee4852b402b","group":"360b409060b23c5b","name":"my LED","order":5,"width":0,"height":0,"format":"<p>it is here -> <i class=\"led\"></i> </p>\n<script>\n    (function(scope) {\n        function sleep(ms) {\n            return new Promise(resolve => setTimeout(resolve, ms));\n        }\n        scope.$watch('msg', function(msg) {\n            if (msg && msg.topic == \"setClass\") {\n                $(\"i.led\")[0].className = 'led'\n                sleep(25).then(() => $(\"i.led\").addClass(msg.payload))\n            } else  if (msg && msg.topic == \"addClass\") {\n                $(\"i.led\").addClass(msg.payload);\n            } else  if (msg && msg.topic == \"removeClass\") {\n                $(\"i.led\").removeClass(msg.payload);\n                $(\"i.led\").addClass('led');\n            } else  if (msg && msg.topic == \"toggleClass\") {\n                $(\"i.led\").toggleClass(msg.payload);\n                $(\"i.led\").addClass('led');\n            }\n        });\n    })(scope);\n</script>","storeOutMessages":true,"fwdInMessages":true,"resendOnRefresh":true,"templateScope":"local","className":"","x":420,"y":120,"wires":[[]]},{"id":"53b4c63491c105f5","type":"ui_template","z":"aaa27ee4852b402b","group":"c3180d6ecfee774b","name":"head CSS","order":3,"width":0,"height":0,"format":"<style>\ni.led {\n  font-size: 1em;\n  box-sizing: border-box;\n  border: 1px solid rgba(0, 0, 0, 0.75);\n  margin: 0;\n  position: relative;\n  display: inline-block;\n  width: 1em;\n  height: 1em;\n  background: #808080;\n  border-radius: 50%;\n  vertical-align: text-bottom;\n  box-shadow: inset 0.25em 0.25em 0.25em rgba(255, 255, 255, 0.30), inset -0.25em -0.25em 0.25em 0.5em rgba(0, 0, 0, 0.30), inset 1em 0.5em 0.5em rgba(0, 0, 0, 0.5);\n}\ni.led:before {\n  content: '';\n  border-radius: 50%;\n  display: block;\n  width: 0.25em;\n  height: 0.25em;\n  background: #fff;\n  opacity: 0.2;\n  margin-top: 0.2em;\n  margin-left: 0.2em;\n  mix-blend-mode: overlay;\n}\ni.led.led-xs {\n  font-size: 50%;\n}\ni.led.led-sm {\n  font-size: 80%;\n}\ni.led.red {\n  background:#ff4500;color:#ff4500;;\n}\ni.led.green {\n  background:#adff2f;\n  color:#adff2f;\n}\ni.led.blue {\n  background:#0046ff;\n  color:#0046ff;\n}\ni.led.yellow {\n  background:#ffff00;\n  color:#ffff00;\n}\ni.led.orange {\n  background:#ffa500;\n  color:#ffa500;\n}\ni.led.white {\n  background:#fafaff;\n  color:#fafaff;\n}\ni.led.on {\n  box-shadow: 0 0 0.25em 0.1em, 0 0 1em 0.5em;\n  border: 2px solid rgba(0, 0, 0, 0.05);\n}\ni.led.on.blink.fast {\n  animation: blink 0.25s forwards infinite;\n}\ni.led.on.blink {\n  animation: blink 0.5s forwards infinite;\n}\ni.led.on.blink.slow {\n  animation: blink 1s forwards infinite;\n}\n\ni.led.on.blink.c1{\n  animation-iteration-count:1;\n}\ni.led.on.blink.c2{\n  animation-iteration-count:2;\n}\ni.led.on.blink.c3{\n  animation-iteration-count:3;\n}\ni.led.on.blink.c5{\n  animation-iteration-count:5;\n}\ni.led.on.blink.c10{\n  animation-iteration-count:10;\n}\n\ni.led.on.blink.t3s{\n  animation-iteration-count:6;\n}\ni.led.on.blink.t3s.fast{\n  animation-iteration-count:12;\n}\ni.led.on.blink.t3s.slow{\n  animation-iteration-count:3;\n}\ni.led.on.blink.t5s{\n  animation-iteration-count:10;\n}\ni.led.on.blink.t5s.fast{\n  animation-iteration-count:20;\n}\ni.led.on.blink.t5s.slow{\n  animation-iteration-count:5;\n}\ni.led.on.blink.t10s{\n  animation-iteration-count:20;\n}\ni.led.on.blink.t10s.fast{\n  animation-iteration-count:40;\n}\ni.led.on.blink.t10s.slow{\n  animation-iteration-count:10;\n}\n\n\n@keyframes blink {\n  0% {\n    border: 1px solid rgba(0, 0, 0, 0.75);\n    box-shadow: inset 0.25em 0.25em 0.25em rgba(255, 255, 255, 0.25), inset -0.25em -0.25em 0.25em 0.5em rgba(0, 0, 0, 0.25), inset 1em 0.5em 0.5em rgba(0, 0, 0, 0.5);\n  }\n  50% {\n    border: 2px solid rgba(0, 0, 0, 0.05);\n    box-shadow: 0 0 0.25em 0.1em, 0 0 1em 0.5em;\n  }\n    100% {\n    border: 1px solid rgba(0, 0, 0, 0.75);\n    box-shadow: inset 0.25em 0.25em 0.25em rgba(255, 255, 255, 0.25), inset -0.25em -0.25em 0.25em 0.5em rgba(0, 0, 0, 0.25), inset 1em 0.5em 0.5em rgba(0, 0, 0, 0.5);\n  } \n}\n\n</style>\n","storeOutMessages":true,"fwdInMessages":true,"resendOnRefresh":true,"templateScope":"global","className":"","x":430,"y":80,"wires":[[]]},{"id":"3e367f3c4d3f757e","type":"ui_button","z":"aaa27ee4852b402b","name":"","group":"360b409060b23c5b","order":6,"width":3,"height":1,"passthru":false,"label":"Red LED (off)","tooltip":"","color":"","bgcolor":"","className":"","icon":"","payload":"red","payloadType":"str","topic":"setClass","topicType":"str","x":200,"y":280,"wires":[["0be84b36fa2cfcd3"]]},{"id":"499f749728283daa","type":"ui_button","z":"aaa27ee4852b402b","name":"","group":"360b409060b23c5b","order":10,"width":3,"height":1,"passthru":false,"label":"Toggle On/Off","tooltip":"","color":"","bgcolor":"","className":"","icon":"","payload":"on","payloadType":"str","topic":"toggleClass","topicType":"str","x":200,"y":440,"wires":[["0be84b36fa2cfcd3"]]},{"id":"6ed0e706fef321be","type":"ui_button","z":"aaa27ee4852b402b","name":"","group":"360b409060b23c5b","order":11,"width":3,"height":1,"passthru":false,"label":"toggle flash","tooltip":"","color":"","bgcolor":"","className":"","icon":"","payload":"blink","payloadType":"str","topic":"toggleClass","topicType":"str","x":190,"y":480,"wires":[["0be84b36fa2cfcd3"]]},{"id":"c0aa87141a6ee725","type":"ui_button","z":"aaa27ee4852b402b","name":"","group":"360b409060b23c5b","order":9,"width":3,"height":1,"passthru":false,"label":"Blue LED (off)","tooltip":"","color":"","bgcolor":"","className":"","icon":"","payload":"blue","payloadType":"str","topic":"setClass","topicType":"str","x":200,"y":400,"wires":[["0be84b36fa2cfcd3"]]},{"id":"92237579ce5a8d30","type":"ui_button","z":"aaa27ee4852b402b","name":"","group":"360b409060b23c5b","order":13,"width":3,"height":1,"passthru":false,"label":"toggle slow","tooltip":"","color":"","bgcolor":"","className":"","icon":"","payload":"slow","payloadType":"str","topic":"toggleClass","topicType":"str","x":190,"y":520,"wires":[["0be84b36fa2cfcd3"]]},{"id":"0b39b675d3e84aa1","type":"ui_button","z":"aaa27ee4852b402b","name":"","group":"360b409060b23c5b","order":12,"width":3,"height":1,"passthru":false,"label":"toggle fast","tooltip":"","color":"","bgcolor":"","className":"","icon":"","payload":"fast","payloadType":"str","topic":"toggleClass","topicType":"str","x":190,"y":560,"wires":[["0be84b36fa2cfcd3"]]},{"id":"a247f12b4c81cc8d","type":"ui_button","z":"aaa27ee4852b402b","name":"","group":"360b409060b23c5b","order":7,"width":3,"height":1,"passthru":false,"label":"Orange LED (off)","tooltip":"","color":"","bgcolor":"","className":"","icon":"","payload":"orange","payloadType":"str","topic":"setClass","topicType":"str","x":210,"y":320,"wires":[["0be84b36fa2cfcd3"]]},{"id":"0aba4e7f8b3a60be","type":"ui_button","z":"aaa27ee4852b402b","name":"","group":"360b409060b23c5b","order":8,"width":3,"height":1,"passthru":false,"label":"Yellow LED (off)","tooltip":"","color":"","bgcolor":"","className":"","icon":"","payload":"yellow","payloadType":"str","topic":"setClass","topicType":"str","x":200,"y":360,"wires":[["0be84b36fa2cfcd3"]]},{"id":"0be84b36fa2cfcd3","type":"link out","z":"aaa27ee4852b402b","name":"link out 17","mode":"link","links":["632eb74eeda4b1a4"],"x":405,"y":380,"wires":[]},{"id":"632eb74eeda4b1a4","type":"link in","z":"aaa27ee4852b402b","name":"link in 10","links":["0be84b36fa2cfcd3"],"x":385,"y":180,"wires":[["808ff37d8b2460e3"]]},{"id":"360b409060b23c5b","type":"ui_group","name":"Demo","tab":"0c531246b2a3f00e","order":1,"disp":true,"width":"12","collapse":false,"className":""},{"id":"c3180d6ecfee774b","type":"ui_group","name":"inventory","tab":"a088985c1d3b62a3","order":1,"disp":true,"width":"12","collapse":false,"className":""},{"id":"0c531246b2a3f00e","type":"ui_tab","name":"Demo","icon":"dashboard","disabled":false,"hidden":false},{"id":"a088985c1d3b62a3","type":"ui_tab","name":"Supermarket","icon":"dashboard","order":1,"disabled":false,"hidden":false}]
7 Likes

That's true but I'm going through a phase of trying not to reach for the Javascript when making a flow.
Your first function solution surely ticks the "minimal screen space" button but it's a cryptic function , not only the pesky => syntax but two of them in a row. :confounded:
I struggled to understand the delay nodes till I realised you are sending all the on/off messages at the start, each with it's own different msg.delay.

OK, it's the perfect place to use a subflow, does the job. Uses functions though, and return [msg, null] or return [null, msg] makes me feel uneasy :grinning:

Yup, very elegant. :white_check_mark:

My only nit-pick is that the CSS only caters for up to 10 flashes, if you send blue on blink c11 fast
it flashes for ever. I wonder if that can be fixed using CSS counters...

That's ok.

Those are only there so if nothing needs to be sent at that time, a null is sent.

Alas as with many things: there are many ways to get it done.

There is a Blinker node ( BLINKER ULTIMATE). I've used to blink a real Led and it works fine.

can you not just use npm install node-red-contrib-ui-led in some way?

Thanks for posting that @podi62, some interesting nodes bundled together there.

I do use the ui-led node on my dashboards, it's pretty good, and I dare say it can be tweeked with CSS to blink, though I haven't tried to. The flow I posted at the top uses this node.

Background to why I raised the question:

I wanted some dashboard actions to require a double button press but give some feedback - sort of "Are you sure?"
To indicate this I flash the led for a short while.
Since it's a simple enough effect I was surprised it took so many nodes to construct the blinking and the result isn't very convincing.

I just wondered how other people would go about doing it.

hi,
Are you using node-red dashboard? if so have you considered the uibuilder node? i am using this and can achieve so much more than the standard dashboard? i am no expert but have managed (with a lot of help from this forum) to achieve just about anything i needed