Chromecast <-> nodered <-> harmony hub remote integration.
-
As I have mentioned elsewhere, I have been working on re-implementing a lot of my automation stuff, which now runs entirely in node-red, with a couple of companion hang-arounds to make the system complete.
The last addition is that I now can control my chromecast with my harmony hub remote (I have a Harmony Elite). Normally this remote can't control chromecasts directly, but with the help of an extra python script, I now have an emulated roku device on my network (which the Harmony is happy to control over WiFI), that emits the "remote" button presses it receives as MQTT topics. This means that I can make node-red flows, that act upon button presses on my harmony remote
In addition I have my python chromecast control script that expose my chromecasts as MQTT devices, so that they emit a MQTT message when status changes, and I can send MQTT messages to the chromecast, such as pause / play etc.
I have chosen to have node-red as a central hub, so I can manipulate message that I receive from the roku, this is because it does not have a separate "pause" functionality, the pause button simply emits "play", and so does the play button. In nodered I keep track on states of the chromecast, if it's in PLAYING state I send pause, otherwise I send play
the roku implementation can be found on github here, and the chromecastcontrol is likewise on github here
Control flow that handles play / pause propagation from roku to av center
[ { "id": "bfa30dc8.1e214", "type": "mqtt out", "z": "d2be3a2a.67d4b8", "name": "", "topic": "avctrl/in/control", "qos": "2", "retain": "false", "broker": "4798f2b0.5d747c", "x": 1120, "y": 280, "wires": [] }, { "id": "d40036e7.baedd8", "type": "mqtt in", "z": "d2be3a2a.67d4b8", "name": "", "topic": "roku/keypress", "qos": "2", "broker": "4798f2b0.5d747c", "x": 770, "y": 280, "wires": [ [ "e7726249.7e6c" ] ] }, { "id": "e7726249.7e6c", "type": "function", "z": "d2be3a2a.67d4b8", "name": "translate roku", "func": "state = global.get('chromestate');\nmsg.payload = msg.payload.toLowerCase();\nif (state == 'PLAYING' && msg.payload == 'play') {\n msg.payload = 'pause';\n} \nif (msg.payload == 'back') {\n msg.payload = 'stop';\n}\nreturn msg;", "outputs": 1, "noerr": 0, "x": 940, "y": 280, "wires": [ [ "bfa30dc8.1e214", "fcfb9705.c45cf8" ] ] }, { "id": "fcfb9705.c45cf8", "type": "debug", "z": "d2be3a2a.67d4b8", "name": "", "active": true, "tosidebar": true, "console": false, "tostatus": false, "complete": "false", "x": 1110, "y": 240, "wires": [] }, { "id": "cc3b35dc.759cb8", "type": "mqtt in", "z": "d2be3a2a.67d4b8", "name": "", "topic": "chromecast/+/state", "qos": "2", "broker": "4798f2b0.5d747c", "x": 750, "y": 340, "wires": [ [ "76b34013.ca6b5" ] ] }, { "id": "76b34013.ca6b5", "type": "change", "z": "d2be3a2a.67d4b8", "name": "", "rules": [ { "t": "set", "p": "chromestate", "pt": "global", "to": "payload", "tot": "msg" } ], "action": "", "property": "", "from": "", "to": "", "reg": false, "x": 960, "y": 340, "wires": [ [ "f1bbbf89.9581d" ] ] }, { "id": "f1bbbf89.9581d", "type": "debug", "z": "d2be3a2a.67d4b8", "name": "", "active": false, "tosidebar": true, "console": false, "tostatus": false, "complete": "false", "x": 1150, "y": 340, "wires": [] }, { "id": "4798f2b0.5d747c", "type": "mqtt-broker", "z": "", "broker": "mosquitto", "port": "1883", "clientid": "nodered-mysensors", "usetls": false, "compatmode": true, "keepalive": "60", "cleansession": true, "willTopic": "", "willQos": "0", "willPayload": "", "birthTopic": "", "birthQos": "0", "birthPayload": "" } ]
And the AV center control flow
[ { "id": "2c73477.9c149b8", "type": "switch", "z": "d2be3a2a.67d4b8", "name": "", "property": "payload", "propertyType": "msg", "rules": [ { "t": "eq", "v": "volumeup", "vt": "str" }, { "t": "eq", "v": "volumedown", "vt": "str" }, { "t": "eq", "v": "mute", "vt": "str" }, { "t": "else" } ], "checkall": "false", "repair": false, "outputs": 4, "x": 290, "y": 480, "wires": [ [ "f5568b5f.898cc8" ], [ "367a93b4.65ee8c" ], [ "c70cb7be.595738" ], [ "fc2bd7be.c40148" ] ] }, { "id": "f5568b5f.898cc8", "type": "H command", "z": "d2be3a2a.67d4b8", "name": "Volume UP", "server": "56b003cf.93c9bc", "activity": "21395657", "label": "Chromecast Audio", "command": "%7B%22command%22::%22VolumeUp%22,%22type%22::%22IRCommand%22,%22deviceId%22::%2237008441%22%7D", "repeat": "1", "x": 490, "y": 400, "wires": [ [] ] }, { "id": "367a93b4.65ee8c", "type": "H command", "z": "d2be3a2a.67d4b8", "name": "Volume DOWN", "server": "56b003cf.93c9bc", "activity": "21395657", "label": "Chromecast Audio", "command": "%7B%22command%22::%22VolumeDown%22,%22type%22::%22IRCommand%22,%22deviceId%22::%2237008441%22%7D", "repeat": "1", "x": 500, "y": 440, "wires": [ [] ] }, { "id": "1bb38ed7.f57a81", "type": "mqtt in", "z": "d2be3a2a.67d4b8", "name": "", "topic": "avctrl/in/control", "qos": "2", "broker": "4798f2b0.5d747c", "x": 120, "y": 480, "wires": [ [ "2c73477.9c149b8", "9b166d59.19dab" ] ] }, { "id": "c70cb7be.595738", "type": "H command", "z": "d2be3a2a.67d4b8", "name": "Mute", "server": "56b003cf.93c9bc", "activity": "21395657", "label": "Chromecast TV", "command": "%7B%22command%22::%22Mute%22,%22type%22::%22IRCommand%22,%22deviceId%22::%2237008441%22%7D", "repeat": "1", "x": 470, "y": 480, "wires": [ [] ] }, { "id": "fc2bd7be.c40148", "type": "switch", "z": "d2be3a2a.67d4b8", "name": "", "property": "avctrl", "propertyType": "global", "rules": [ { "t": "eq", "v": "dvd", "vt": "str" }, { "t": "eq", "v": "cd", "vt": "str" }, { "t": "cont", "v": "audio", "vt": "str" }, { "t": "cont", "v": "video", "vt": "str" } ], "checkall": "false", "repair": false, "outputs": 4, "x": 470, "y": 540, "wires": [ [ "25273a45.75e776" ], [ "25273a45.75e776" ], [ "e0cb35b0.2bf4a8" ], [ "fef6e906.107c58" ] ] }, { "id": "e0cb35b0.2bf4a8", "type": "change", "z": "d2be3a2a.67d4b8", "name": "audio", "rules": [ { "t": "change", "p": "topic", "pt": "msg", "from": "avctrl/in", "fromt": "str", "to": "chromecast/audio", "tot": "str" } ], "action": "", "property": "", "from": "", "to": "", "reg": false, "x": 610, "y": 560, "wires": [ [ "15cbe8a1.e953e7", "2aadd645.74b1ba" ] ] }, { "id": "fef6e906.107c58", "type": "change", "z": "d2be3a2a.67d4b8", "name": "video", "rules": [ { "t": "change", "p": "topic", "pt": "msg", "from": "avctrl/in", "fromt": "str", "to": "chromecast/cast", "tot": "str" } ], "action": "", "property": "", "from": "", "to": "", "reg": false, "x": 610, "y": 600, "wires": [ [ "15cbe8a1.e953e7", "2aadd645.74b1ba" ] ] }, { "id": "15cbe8a1.e953e7", "type": "mqtt out", "z": "d2be3a2a.67d4b8", "name": "", "topic": "", "qos": "2", "retain": "false", "broker": "4798f2b0.5d747c", "x": 770, "y": 620, "wires": [] }, { "id": "25273a45.75e776", "type": "switch", "z": "d2be3a2a.67d4b8", "name": "", "property": "payload", "propertyType": "msg", "rules": [ { "t": "eq", "v": "pause", "vt": "str" }, { "t": "eq", "v": "play", "vt": "str" }, { "t": "eq", "v": "fwd", "vt": "str" }, { "t": "eq", "v": "rev", "vt": "str" }, { "t": "eq", "v": "stop", "vt": "str" } ], "checkall": "false", "repair": false, "outputs": 5, "x": 610, "y": 500, "wires": [ [ "c29005a6.029498" ], [ "aa76e025.eb137" ], [ "b3ccceab.07286" ], [ "a0ecd37f.01d88" ], [ "a73cb364.f49ab" ] ] }, { "id": "c29005a6.029498", "type": "H command", "z": "d2be3a2a.67d4b8", "name": "Pause", "server": "56b003cf.93c9bc", "activity": "20800253", "label": "DVD", "command": "%7B%22command%22::%22Pause%22,%22type%22::%22IRCommand%22,%22deviceId%22::%2237008443%22%7D", "repeat": "1", "x": 770, "y": 420, "wires": [ [] ] }, { "id": "aa76e025.eb137", "type": "H command", "z": "d2be3a2a.67d4b8", "name": "Play", "server": "56b003cf.93c9bc", "activity": "20800253", "label": "DVD", "command": "%7B%22command%22::%22Play%22,%22type%22::%22IRCommand%22,%22deviceId%22::%2237008443%22%7D", "repeat": "1", "x": 770, "y": 460, "wires": [ [] ] }, { "id": "a73cb364.f49ab", "type": "H command", "z": "d2be3a2a.67d4b8", "name": "Stop", "server": "56b003cf.93c9bc", "activity": "20800253", "label": "DVD", "command": "%7B%22command%22::%22Stop%22,%22type%22::%22IRCommand%22,%22deviceId%22::%2237008443%22%7D", "repeat": "1", "x": 770, "y": 580, "wires": [ [] ] }, { "id": "9b166d59.19dab", "type": "debug", "z": "d2be3a2a.67d4b8", "name": "", "active": false, "tosidebar": true, "console": false, "tostatus": false, "complete": "true", "x": 290, "y": 420, "wires": [] }, { "id": "ff1c1d34.07702", "type": "comment", "z": "d2be3a2a.67d4b8", "name": "MQTT -> control media endpoint", "info": "", "x": 170, "y": 380, "wires": [] }, { "id": "b3ccceab.07286", "type": "H command", "z": "d2be3a2a.67d4b8", "name": "Skip fwd", "server": "56b003cf.93c9bc", "activity": "20800253", "label": "DVD", "command": "%7B%22command%22::%22ChapterNext%22,%22type%22::%22IRCommand%22,%22deviceId%22::%2237008443%22%7D", "repeat": "1", "x": 780, "y": 500, "wires": [ [] ] }, { "id": "a0ecd37f.01d88", "type": "H command", "z": "d2be3a2a.67d4b8", "name": "Skip back", "server": "56b003cf.93c9bc", "activity": "20800253", "label": "DVD", "command": "%7B%22command%22::%22ChapterPrev%22,%22type%22::%22IRCommand%22,%22deviceId%22::%2237008443%22%7D", "repeat": "1", "x": 780, "y": 540, "wires": [ [] ] }, { "id": "2aadd645.74b1ba", "type": "debug", "z": "d2be3a2a.67d4b8", "name": "", "active": true, "tosidebar": true, "console": false, "tostatus": false, "complete": "true", "x": 980, "y": 620, "wires": [] }, { "id": "56b003cf.93c9bc", "type": "harmony-server", "z": "", "name": "Stuen", "ip": "192.168.0.66" }, { "id": "4798f2b0.5d747c", "type": "mqtt-broker", "z": "", "broker": "mosquitto", "port": "1883", "clientid": "nodered-mysensors", "usetls": false, "compatmode": true, "keepalive": "60", "cleansession": true, "willTopic": "", "willQos": "0", "willPayload": "", "birthTopic": "", "birthQos": "0", "birthPayload": "" } ]