A working example to Simulate a Flickering Fire

My £450 electric flicker fire has blown up, I don't use the actual electric heating part, but it was nice to have a flickering flame, which made it look warm in the lounge.

I couldn't find a new fire that would fit n the hole as it is built in.
So I bought a couple of WIZ Philips bulbs for £13 each, cheaper than the Hue Philips lamps which would have cost £49 each and thought, if I could make them flicker it would give a similar effect to the original mechanical fan blowing across ribbons. (Don't laugh it was 21 years old!!!).

What I didn't realise is that the WIZ are very inferior to the Hue lamps and the app for the Wiz lamps is awful including a fireplace setting that is ridiculous.

So NODE-RED to the rescue, which flickers each of the two lamps independently and alters the intensity with a dash of a blue flame every now and then.
To switch it on and off I am using a Sonoff MQTT button and that is controlled via Home Assistant automation, which you can strip out if not required and use some other way to control it. I did ask ChatGPT for a bit of help, especially for the WIZ configuration.

Here is my code, just in case anyone is interested, which although not perfect, it adds a warm feeling to the room.

[{"id":"5bf36940c9237a49","type":"group","z":"82d6991d3a25e57f","name":"WIZ LAMP FIRE FLICKER GERNERATOR","style":{"label":true,"color":"#0070c0"},"nodes":["19be96593a3461d8","2f74f743024cf292","d604c024a3f2ae10","c48d8dd09cf669ed","11d43c9b0d8ba91e","ef467243a1aeb278","723c9f8a34372edd","8c304212c79d3040","6c5a4456c65e322f","261e9c93ce4ada5a","c447a346903cf6f4"],"x":34,"y":39,"w":912,"h":262},{"id":"19be96593a3461d8","type":"inject","z":"82d6991d3a25e57f","g":"5bf36940c9237a49","name":"🔥 Start Flicker","props":[],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":160,"y":80,"wires":[["c48d8dd09cf669ed"]]},{"id":"2f74f743024cf292","type":"inject","z":"82d6991d3a25e57f","g":"5bf36940c9237a49","name":"🛑 Stop Flicker","props":[],"repeat":"","once":false,"onceDelay":0.1,"topic":"","payloadType":"date","x":160,"y":200,"wires":[["11d43c9b0d8ba91e"]]},{"id":"d604c024a3f2ae10","type":"inject","z":"82d6991d3a25e57f","g":"5bf36940c9237a49","name":"Ticker 0.8","props":[],"repeat":"0.8","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":150,"y":260,"wires":[["c447a346903cf6f4"]]},{"id":"c48d8dd09cf669ed","type":"function","z":"82d6991d3a25e57f","g":"5bf36940c9237a49","name":"Start Flicker","func":"global.set(\"wiz_stop\", false);\nnode.status({ fill: \"green\", shape: \"dot\", text: \"Running\" });\nreturn null;","outputs":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":610,"y":80,"wires":[]},{"id":"11d43c9b0d8ba91e","type":"function","z":"82d6991d3a25e57f","g":"5bf36940c9237a49","name":"Stop Flicker","func":"global.set(\"wiz_stop\", true);\nnode.status({ fill: \"red\", shape: \"dot\", text: \"Stopped\" });\nreturn null;","outputs":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":610,"y":200,"wires":[]},{"id":"ef467243a1aeb278","type":"udp out","z":"82d6991d3a25e57f","g":"5bf36940c9237a49","name":"WiZ UDP Out","addr":"","iface":"","port":"38899","ipv":"udp4","outport":"","base64":false,"multicast":"false","x":680,"y":260,"wires":[]},{"id":"723c9f8a34372edd","type":"mqtt out","z":"82d6991d3a25e57f","g":"5bf36940c9237a49","name":"","topic":"fire","qos":"","retain":"true","respTopic":"","contentType":"","userProps":"","correl":"","expiry":"","broker":"193b5e1a560af32d","x":590,"y":140,"wires":[]},{"id":"8c304212c79d3040","type":"mqtt in","z":"82d6991d3a25e57f","g":"5bf36940c9237a49","name":"auto button2","topic":"firecommand","qos":"0","datatype":"auto-detect","broker":"c58ee1278a911514","nl":false,"rap":true,"rh":0,"inputs":0,"x":230,"y":140,"wires":[["6c5a4456c65e322f"]]},{"id":"6c5a4456c65e322f","type":"switch","z":"82d6991d3a25e57f","g":"5bf36940c9237a49","name":"On/Off","property":"payload","propertyType":"msg","rules":[{"t":"true"},{"t":"false"}],"checkall":"true","repair":false,"outputs":2,"x":410,"y":140,"wires":[["723c9f8a34372edd","c48d8dd09cf669ed","261e9c93ce4ada5a"],["723c9f8a34372edd","11d43c9b0d8ba91e","261e9c93ce4ada5a"]]},{"id":"261e9c93ce4ada5a","type":"debug","z":"82d6991d3a25e57f","g":"5bf36940c9237a49","name":"debug 17","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":840,"y":140,"wires":[]},{"id":"c447a346903cf6f4","type":"function","z":"82d6991d3a25e57f","g":"5bf36940c9237a49","name":"FINAL - Fire Flicker  with Blue","func":"const lampIPs = [\"192.168.1.26\", \"192.168.1.27\"];\n\n// Dimming range\nconst minD = 30;\nconst maxD = 60;\n\n// Color temperature range in Kelvin\nconst minTemp = 2200; //2200\nconst maxTemp = 3000; //2700\n\n// Stop if flicker disabled\nif (global.get(\"wiz_stop\")) return null;\n\n// Build messages for each lamp with independent dimming/temp\nconst msgs = lampIPs.map(ip => {\n    const dimming = Math.floor(Math.random() * (maxD - minD + 1)) + minD;\n\n    let params;\n\n    // 💙 2% chance of blue flash\n    if (Math.random() < 0.02) {\n        params = {\n            state: true,\n            r: 0,\n            g: 0,\n            b: 255,\n            dimming: 100 // full brightness so you actually see it\n        };\n    } else {\n        const temp = Math.floor(Math.random() * (maxTemp - minTemp + 1)) + minTemp;\n        params = {\n            state: true,\n            dimming: dimming,\n            temp: temp\n        };\n    }\n\n    return {\n        payload: JSON.stringify({\n            method: \"setState\",\n            params: params\n        }),\n        ip: ip,\n        port: 38899,\n        // each lamp has its own random interval 500 900\n        nextTick: 400 + Math.random() * 1000\n    };\n});\n\n// Optional: show the last values in node status\nnode.status({\n    fill: \"orange\",\n    shape: \"dot\",\n    text: msgs.map(m => JSON.parse(m.payload)).map(p => {\n        const pParams = p.params;\n        if (\"r\" in pParams) {\n            return `💙 Blue flash`;\n        } else {\n            return `D=${pParams.dimming} T=${pParams.temp}`;\n        }\n    }).join(\" | \")\n});\n\n// Send each lamp individually, with its own tick\nmsgs.forEach(m => {\n    node.send([m]); // send to UDP node for that lamp\n    setTimeout(() => {\n        if (!global.get(\"wiz_stop\")) {\n            node.send([m]);\n        } else {\n            node.status({ fill: \"grey\", shape: \"dot\", text: \"Stopped\" });\n        }\n    }, m.nextTick);\n});\n\nreturn null;\n","outputs":1,"timeout":"","noerr":0,"initialize":"","finalize":"","libs":[],"x":400,"y":260,"wires":[["ef467243a1aeb278"]]},{"id":"193b5e1a560af32d","type":"mqtt-broker","name":"","broker":"192.168.1.141","port":1883,"clientid":"fire","autoConnect":true,"usetls":false,"protocolVersion":4,"keepalive":60,"cleansession":true,"autoUnsubscribe":true,"birthTopic":"","birthQos":"0","birthRetain":"false","birthPayload":"","birthMsg":{},"closeTopic":"","closeQos":"0","closeRetain":"false","closePayload":"","closeMsg":{},"willTopic":"","willQos":"0","willRetain":"false","willPayload":"","willMsg":{},"userProps":"","sessionExpiry":""},{"id":"c58ee1278a911514","type":"mqtt-broker","name":"","broker":"192.168.1.130","port":1883,"clientid":"","autoConnect":true,"usetls":false,"protocolVersion":4,"keepalive":60,"cleansession":true,"autoUnsubscribe":true,"birthTopic":"","birthQos":"0","birthRetain":"false","birthPayload":"","birthMsg":{},"closeTopic":"","closeQos":"0","closeRetain":"false","closePayload":"","closeMsg":{},"willTopic":"","willQos":"0","willRetain":"false","willPayload":"","willMsg":{},"userProps":"","sessionExpiry":""}]

5 Likes

I love it - such a gorgeously over-the-top solution. :rofl:

1 Like