Rhasspy + Node Red + Home Assistant = Smart Speaker Jr

A TLDR background, I used to run Snips with a few Node Red skills. Snips was killed and I tried Mycroft (which failed to recognize my wife or kids), and then I stumbled upon Rhasspy.
I struggled getting Rhasspy and HA to play nicely and then I remembered Node Red.

So over the past few weeks I’ve dug into Rhasspy, NR, and HA and I’ve been able to quickly develop some ‘skills’ that I find useful, and a few that are ‘just because’.

My first set of skills uses HA elements to provide the current weather conditions, the forecast, the outside and inside temperatures, turning on and off lights, and setting the thermostat temperature.

My second set of skills revolves around playing/streaming internet radio stations to our Chromecast audio. I can ask Rhasspy to play a station, pause, resume, or stop playback. I can also ask Rhasspy to mute/unmute, or set the volume to a value between 0 and 10.

Skill set 3 started out as a Good Morning skills which gives the date and time, along with the weather forecast. Originally it also played the News but I moved that out on it’s own and added Good Afternoon, and Good Evening.

The last skill set I’ve create is the News and Joke set. Asking for the News has NR pulling the Hourly News mp3 from CBC, converting it on the fly to wav and then playing it back.
A joke request grabs a classic Dad joke from an online database and uses Rhasspy’s text to speech to delivery a joke that makes every dad smile and kids groan.

I can absolutely elaborate and share Rhasspy setup, NR flows if there is interest.

1 Like

Please do share it, because people are always in need of good examples.

Maybe you have read the wiki page already, but that might give you also some new insights.
It is mainly how I have setup Node Red and HA, but who knows :slight_smile:

Here is my first flow. I am sure there are efficiencies that could be found but it works.
I tried to keep it as clean as possible but it does get a bit messy around the lights.

and the Node-Red flow export:

[{"id":"bb01177afb3efad0","type":"websocket in","z":"e17061c5369e4b2b","name":"Rhasspy In","server":"af92e4f2.028968","client":"","x":80,"y":140,"wires":[["6af596d8414a6121"]]},{"id":"6af596d8414a6121","type":"switch","z":"e17061c5369e4b2b","name":"","property":"intent.name","propertyType":"msg","rules":[{"t":"eq","v":"GetTime","vt":"str"},{"t":"eq","v":"GetForecast","vt":"str"},{"t":"eq","v":"GetWeather","vt":"str"},{"t":"eq","v":"GetTemperature","vt":"str"},{"t":"eq","v":"TurnState","vt":"str"},{"t":"eq","v":"Goodnight","vt":"str"},{"t":"eq","v":"SetTemperature","vt":"str"},{"t":"else"}],"checkall":"false","repair":false,"outputs":8,"x":90,"y":260,"wires":[["c5c2535d4f57eefc"],["a177a9c6ceba6fe0"],["d398c11b7bf7ec79","65af43da39379e01"],["2d3a12859ffe8b53","50490adb392afc13"],["8df0105aac81fe79"],["899f888e7f9d1c76"],["9cb024e068df3a81"],["8518cd2330e9e245"]]},{"id":"2d3a12859ffe8b53","type":"api-current-state","z":"e17061c5369e4b2b","name":"House Temperature","server":"890f6b21.aaf278","version":2,"outputs":1,"halt_if":"","halt_if_type":"str","halt_if_compare":"is_not","entity_id":"sensor.house_temperature","state_type":"str","blockInputOverrides":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"entity"}],"x":310,"y":380,"wires":[["649a9c6bbb64afe8"]]},{"id":"50490adb392afc13","type":"api-current-state","z":"e17061c5369e4b2b","name":"Outside Temperature","server":"890f6b21.aaf278","version":2,"outputs":1,"halt_if":"","halt_if_type":"num","halt_if_compare":"is_not","entity_id":"sensor.temperature","state_type":"str","blockInputOverrides":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"entity"}],"x":300,"y":440,"wires":[["fd21be261c32e191"]]},{"id":"fd21be261c32e191","type":"function","z":"e17061c5369e4b2b","name":"Outside Temp prefix","func":"msg.payload = \"It is \" + msg.payload + \" degrees outside\";\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":520,"y":440,"wires":[["f3a36540080a5c29"]]},{"id":"649a9c6bbb64afe8","type":"function","z":"e17061c5369e4b2b","name":"House Temp prefix","func":"msg.payload = \"and \" + msg.payload + \" degrees inside\";\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":510,"y":380,"wires":[["8fe94ea5cf3f4040"]]},{"id":"f3a36540080a5c29","type":"change","z":"e17061c5369e4b2b","name":"","rules":[{"t":"set","p":"topic","pt":"msg","to":"A","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":710,"y":440,"wires":[["664c2682c5d9273b"]]},{"id":"8fe94ea5cf3f4040","type":"change","z":"e17061c5369e4b2b","name":"","rules":[{"t":"set","p":"topic","pt":"msg","to":"B","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":710,"y":380,"wires":[["664c2682c5d9273b"]]},{"id":"664c2682c5d9273b","type":"join","z":"e17061c5369e4b2b","name":"Assemble Output Text","mode":"custom","build":"object","property":"payload","propertyType":"msg","key":"topic","joiner":" ","joinerType":"str","accumulate":false,"timeout":"","count":"2","reduceRight":false,"reduceExp":"","reduceInit":"","reduceInitType":"","reduceFixup":"","x":900,"y":400,"wires":[["cb4a15fd8818b2b0"]]},{"id":"a413ae6ef73bb123","type":"debug","z":"e17061c5369e4b2b","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":1590,"y":620,"wires":[]},{"id":"c5c2535d4f57eefc","type":"function","z":"e17061c5369e4b2b","name":"time text","func":"var timeString = new Date().toLocaleTimeString([],\n{\n    hour: \"2-digit\", \n    minute: \"2-digit\",\n    hour12: true\n})\n\nreturn {\n    payload: \"It is \" + timeString\n}","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":480,"y":60,"wires":[["30dcf62f8d4df3f3"]]},{"id":"30dcf62f8d4df3f3","type":"http request","z":"e17061c5369e4b2b","name":"text to speech","method":"POST","ret":"txt","paytoqs":"ignore","url":"http://internal-ip:12101/api/text-to-speech","tls":"","persist":false,"proxy":"","authType":"basic","senderr":false,"credentials":{},"x":1540,"y":500,"wires":[[]]},{"id":"cb4a15fd8818b2b0","type":"template","z":"e17061c5369e4b2b","name":"Temperature Speech","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"{{payload.A}} {{payload.B}}","output":"str","x":1120,"y":400,"wires":[["30dcf62f8d4df3f3"]]},{"id":"a177a9c6ceba6fe0","type":"api-current-state","z":"e17061c5369e4b2b","name":"","server":"890f6b21.aaf278","version":2,"outputs":1,"halt_if":"","halt_if_type":"str","halt_if_compare":"is","entity_id":"sensor.forecast","state_type":"str","blockInputOverrides":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"entity"}],"override_topic":false,"state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","x":410,"y":160,"wires":[["30dcf62f8d4df3f3"]]},{"id":"d398c11b7bf7ec79","type":"api-current-state","z":"e17061c5369e4b2b","name":"","server":"890f6b21.aaf278","version":2,"outputs":1,"halt_if":"","halt_if_type":"str","halt_if_compare":"is","entity_id":"weather.location","state_type":"str","blockInputOverrides":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"entity"}],"override_topic":false,"state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","x":410,"y":240,"wires":[["4f38578b391130c6"]]},{"id":"65af43da39379e01","type":"api-current-state","z":"e17061c5369e4b2b","name":"Outside Temperature","server":"890f6b21.aaf278","version":2,"outputs":1,"halt_if":"","halt_if_type":"num","halt_if_compare":"is_not","entity_id":"sensor.temperature","state_type":"str","blockInputOverrides":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"entity"}],"x":380,"y":280,"wires":[["5b10193a941e46bb"]]},{"id":"4f38578b391130c6","type":"function","z":"e17061c5369e4b2b","name":"Weather Conditions Prefix","func":"msg.payload = \"It is currently \" + msg.payload;\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":670,"y":240,"wires":[["48197537cf56307a"]]},{"id":"5b10193a941e46bb","type":"function","z":"e17061c5369e4b2b","name":"Weather Temp prefix","func":"msg.payload = \" and \" + msg.payload + \" degrees\";\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":660,"y":280,"wires":[["3ea0bc0186980f2a"]]},{"id":"48197537cf56307a","type":"change","z":"e17061c5369e4b2b","name":"","rules":[{"t":"set","p":"topic","pt":"msg","to":"A","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":870,"y":240,"wires":[["525f01efd4c09078"]]},{"id":"3ea0bc0186980f2a","type":"change","z":"e17061c5369e4b2b","name":"","rules":[{"t":"set","p":"topic","pt":"msg","to":"B","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":870,"y":280,"wires":[["525f01efd4c09078"]]},{"id":"525f01efd4c09078","type":"join","z":"e17061c5369e4b2b","name":"Assemble Output Text","mode":"custom","build":"object","property":"payload","propertyType":"msg","key":"topic","joiner":" ","joinerType":"str","accumulate":false,"timeout":"","count":"2","reduceRight":false,"reduceExp":"","reduceInit":"","reduceInitType":"","reduceFixup":"","x":1060,"y":260,"wires":[["a8284377ab8dd819"]]},{"id":"a8284377ab8dd819","type":"template","z":"e17061c5369e4b2b","name":"Weather Speech","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"{{payload.A}} {{payload.B}}","output":"str","x":1270,"y":260,"wires":[["30dcf62f8d4df3f3"]]},{"id":"5aad4df0390ecf45","type":"api-call-service","z":"e17061c5369e4b2b","name":"Turn Floor Lamp Off","server":"890f6b21.aaf278","version":3,"debugenabled":false,"service_domain":"light","service":"turn_off","entityId":"light.living_room_lamp_1, light.living_room_lamp_2 ","data":"{\"transition\":60}","dataType":"jsonata","mergecontext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":640,"y":720,"wires":[[]]},{"id":"52305dfb4aba69b5","type":"switch","z":"e17061c5369e4b2b","name":"Plug or Light","property":"slots.object","propertyType":"msg","rules":[{"t":"cont","v":"plug","vt":"str"},{"t":"else"}],"checkall":"true","repair":false,"outputs":2,"x":390,"y":560,"wires":[["61e3b9650ae9b86d"],["9ec726a7988edc60"]]},{"id":"39ce83d5864bc6f7","type":"template","z":"e17061c5369e4b2b","name":"single light","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"{\n  \"domain\": \"light\",\n  \"service\": \"turn_{{slots.state}}\",\n  \"data\": {\n  \"entity_id\": \"light.{{slots.room}}_{{slots.object}}_1\"\n  }\n}","output":"json","x":850,"y":560,"wires":[["6080756c10216a2c","a36a9b75528acad9"]]},{"id":"8df0105aac81fe79","type":"function","z":"e17061c5369e4b2b","name":"","func":"if (msg.slots.object) {\n    msg.slots.object = msg.slots.object.replace(\" \", \"_\");\n}\n\nif (msg.slots.object) {\n    msg.slots.object = msg.slots.object.replace(\"table_lamp\", \"plug_1\")\n}\n\nif (msg.slots.object) {\n    msg.slots.object = msg.slots.object.replace(\"holiday_lights\", \"holiday_wled\")\n}\n\nif (msg.slots.object) {\n    msg.slots.object = msg.slots.object.replace(\"light\", \"lamp\")\n}\n\nif (msg.slots.room) {\n    msg.slots.room = msg.slots.room.replace(\" \", \"_\");\n}\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":240,"y":560,"wires":[["52305dfb4aba69b5"]]},{"id":"61e3b9650ae9b86d","type":"template","z":"e17061c5369e4b2b","name":"single plug","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"{\n  \"domain\": \"switch\",\n  \"service\": \"turn_{{slots.state}}\",\n  \"data\": {\n    \"entity_id\": \"switch.{{slots.object}}\"\n  }\n}\n","output":"json","x":670,"y":500,"wires":[["9969e4fb9d8140c0","a36a9b75528acad9"]]},{"id":"a36a9b75528acad9","type":"api-call-service","z":"e17061c5369e4b2b","name":"Execute","server":"890f6b21.aaf278","version":3,"debugenabled":false,"service_domain":"","service":"","entityId":"","data":"","dataType":"json","mergecontext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":1080,"y":540,"wires":[[]]},{"id":"6080756c10216a2c","type":"template","z":"e17061c5369e4b2b","name":"lights on speech","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"ok, Turning {{slots.state}} the {{entities.1.value}}{{slots.object}}","output":"str","x":1140,"y":600,"wires":[["30dcf62f8d4df3f3"]]},{"id":"9969e4fb9d8140c0","type":"template","z":"e17061c5369e4b2b","name":"table lamp on speech","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"ok, turning on the table lamp","output":"str","x":900,"y":500,"wires":[["30dcf62f8d4df3f3"]]},{"id":"bf9e7ff07de6325a","type":"comment","z":"e17061c5369e4b2b","name":"Turn on and off the lights","info":"","x":220,"y":520,"wires":[]},{"id":"1451d218e5a537e3","type":"comment","z":"e17061c5369e4b2b","name":"Inside and outside temperatures","info":"","x":340,"y":340,"wires":[]},{"id":"190924da1c3d3bbb","type":"comment","z":"e17061c5369e4b2b","name":"Current weather forecast","info":"","x":360,"y":120,"wires":[]},{"id":"e998f75a8b9f2b4d","type":"api-call-service","z":"e17061c5369e4b2b","name":"Turn Table Lamp Off","server":"890f6b21.aaf278","version":3,"debugenabled":false,"service_domain":"switch","service":"turn_off","entityId":"switch.plug_1","data":"","dataType":"jsonata","mergecontext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":640,"y":760,"wires":[[]]},{"id":"51262329e11a9c1d","type":"delay","z":"e17061c5369e4b2b","name":"","pauseType":"delay","timeout":"60","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"allowrate":false,"outputs":1,"x":420,"y":740,"wires":[["5aad4df0390ecf45","e998f75a8b9f2b4d"]]},{"id":"899f888e7f9d1c76","type":"template","z":"e17061c5369e4b2b","name":"Goodnight Speech","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"Goodnight, turning off the lights in one minute","output":"str","x":250,"y":740,"wires":[["30dcf62f8d4df3f3","51262329e11a9c1d"]]},{"id":"9ec726a7988edc60","type":"switch","z":"e17061c5369e4b2b","name":"All or Single","property":"slots.all","propertyType":"msg","rules":[{"t":"eq","v":"all","vt":"str"},{"t":"else"}],"checkall":"false","repair":false,"outputs":2,"x":550,"y":560,"wires":[["ad34cad2fb6e18dd"],["8b6ec50dac68a859"]]},{"id":"ad34cad2fb6e18dd","type":"switch","z":"e17061c5369e4b2b","name":"All lights on/off","property":"slots.state","propertyType":"msg","rules":[{"t":"eq","v":"on","vt":"str"},{"t":"eq","v":"off","vt":"str"}],"checkall":"true","repair":false,"outputs":2,"x":560,"y":640,"wires":[["d4d9e5cf758d6095","57f7ae274c18e77e","2a400547d1fef6b6"],["d4d9e5cf758d6095","5aad4df0390ecf45","e998f75a8b9f2b4d"]]},{"id":"57f7ae274c18e77e","type":"api-call-service","z":"e17061c5369e4b2b","name":"Turn Floor Lamp On","server":"890f6b21.aaf278","version":3,"debugenabled":false,"service_domain":"light","service":"turn_on","entityId":"light.living_room_lamp_1, light.living_room_lamp_2 ","data":"{\"transition\":60}","dataType":"jsonata","mergecontext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":880,"y":660,"wires":[[]]},{"id":"2a400547d1fef6b6","type":"api-call-service","z":"e17061c5369e4b2b","name":"Turn Table Lamp On","server":"890f6b21.aaf278","version":3,"debugenabled":false,"service_domain":"switch","service":"turn_on","entityId":"switch.plug_1","data":"","dataType":"jsonata","mergecontext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":880,"y":700,"wires":[[]]},{"id":"d4d9e5cf758d6095","type":"template","z":"e17061c5369e4b2b","name":"All lights speech","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"ok, Turning {{entities.0.value}} {{entities.1.value}} the {{entities.2.value}}{{entities.3.value}}","output":"str","x":880,"y":760,"wires":[["30dcf62f8d4df3f3"]]},{"id":"b5e37ce5b26beea8","type":"template","z":"e17061c5369e4b2b","name":"Temperature Change Speech","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"Ok, setting the temperature to {{slots.degrees}} degrees","output":"str","x":600,"y":880,"wires":[["30dcf62f8d4df3f3"]]},{"id":"946207e55784e7d7","type":"api-call-service","z":"e17061c5369e4b2b","name":"Set temperature","server":"890f6b21.aaf278","version":3,"debugenabled":true,"service_domain":"climate","service":"set_temperature","entityId":"climate.thermostat_73_db_cc","data":"","dataType":"jsonata","mergecontext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":880,"y":920,"wires":[[]]},{"id":"9cb024e068df3a81","type":"function","z":"e17061c5369e4b2b","name":"Phonetic to Numeric Replace","func":"const lookup = { // add more lookups for other numbers\n    \"thirty\": 30,\n    \"twenty\": 20, \n    \"nine\": 9,\n    \"eight\": 8,\n    \"seven\": 7,\n    \"six\": 6,\n    \"five\": 5,\n    \"four\": 4,\n    \"three\": 3,\n    \"two\": 2,\n    \"one\": 1,\n    \"nineteen\": 19,\n    \"eighteen\": 18,\n    \"seventeen\": 17,\n    \"sixteen\": 16,\n    \"fifteen\": 15,\n    \"fourteen\": 14,\n    \"thirteen\": 13,\n    \"twelve\": 12,\n    \"eleven\": 11,\n    \"ten\": 10,\n    \"point five\": 0.5,\n    \"and a half\": 0.5\n};\nlet pattern = RegExp(Object.keys(lookup).join(\"|\"), \"gi\")\nlet look_up_keys = msg.slots.degrees.match(pattern);\nlet sum = look_up_keys.reduce((a,v) => a+lookup[v.toLowerCase()],0);\nmsg.slots.degrees = msg.slots.degrees.replace(look_up_keys.join(\" \"), sum);\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":280,"y":920,"wires":[["b5e37ce5b26beea8","97b58311f59b347b"]]},{"id":"97b58311f59b347b","type":"template","z":"e17061c5369e4b2b","name":"Temperature Change Template","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"{\n    \"data\": {\n    \"temperature\":{{slots.degrees}}\n  }\n}","output":"str","x":610,"y":920,"wires":[["946207e55784e7d7"]]},{"id":"ecbbf2447e95fcbb","type":"comment","z":"e17061c5369e4b2b","name":"Change the Thermostat Temperature Setting","info":"","x":290,"y":840,"wires":[]},{"id":"8b6ec50dac68a859","type":"switch","z":"e17061c5369e4b2b","name":"Holiday?","property":"slots.object","propertyType":"msg","rules":[{"t":"neq","v":"holiday_wled","vt":"str"},{"t":"else"}],"checkall":"true","repair":false,"outputs":2,"x":700,"y":560,"wires":[["39ce83d5864bc6f7"],["702aaf9227d7ed78"]]},{"id":"702aaf9227d7ed78","type":"template","z":"e17061c5369e4b2b","name":"Holiday lights","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"{\n  \"domain\": \"light\",\n  \"service\": \"turn_{{slots.state}}\",\n  \"data\": {\n  \"entity_id\": \"light.{{slots.object}}\"\n  }\n}","output":"json","x":850,"y":600,"wires":[["401f72da3f67a2de","a36a9b75528acad9"]]},{"id":"401f72da3f67a2de","type":"template","z":"e17061c5369e4b2b","name":"Holiday lights on speech","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"ok, Turning {{slots.state}} the {{entities.1.value}}","output":"str","x":1170,"y":640,"wires":[["30dcf62f8d4df3f3"]]},{"id":"8518cd2330e9e245","type":"link out","z":"e17061c5369e4b2b","name":"","mode":"link","links":["c855eb911347c7f9"],"x":115,"y":1040,"wires":[]},{"id":"fdf0440997c3a4ea","type":"link in","z":"e17061c5369e4b2b","name":"Speech Request In","links":["7b69d47055c23024","885921b779d12688","c2d1bddbe1d79c3e"],"x":1415,"y":800,"wires":[["30dcf62f8d4df3f3","a413ae6ef73bb123"]]},{"id":"be14b496ce11a660","type":"comment","z":"e17061c5369e4b2b","name":"Speech Request In from Other Flows","info":"","x":1400,"y":860,"wires":[]},{"id":"481b2a48538b290c","type":"comment","z":"e17061c5369e4b2b","name":"Unknown Sending to Play Radio","info":"","x":230,"y":1000,"wires":[]},{"id":"af92e4f2.028968","type":"websocket-listener","path":"ws://internal-ip:12101/api/events/intent","wholemsg":"true"},{"id":"890f6b21.aaf278","type":"server","name":"Home Assistant","version":2,"addon":false,"rejectUnauthorizedCerts":true,"ha_boolean":"y|yes|true|on|home|open","connectionDelay":true,"cacheJson":true,"heartbeat":false,"heartbeatInterval":"30"}]

For this flow the only add-on node I am using is the Home Assistant Websocket (node-red-contrib-home-assistant-websocket)

Not sure if I read that before but it was a good read. I’m using websockets right now but I’m considering MQTT.

Nope, it is up just a week or so :smiley:

Nice flow, maybe you can declutter it as well by using subflows. I always like to keep a good overview so when I want to change something I do not have to check all the nodes. I see a couple of potential reusable subflows there :slight_smile:

Good work, I think I will tinker with it a bit!

This is my second flow. It’s the internet Radio streaming and playback control flow.
This flow uses the node-red-contrib-castv2 plugin node.

Here is a screenshot of the flow:

Here is the flow code:

[{"id":"3ffcf753.a145a","type":"switch","z":"96557cc2.7f3b1","name":"Radio Stations","property":"slots.station","propertyType":"msg","rules":[{"t":"eq","v":"The Edge","vt":"str"},{"t":"cont","v":"Boom","vt":"str"},{"t":"eq","v":"My FM","vt":"str"},{"t":"eq","v":"Q107","vt":"str"},{"t":"cont","v":"Fresh","vt":"str"},{"t":"cont","v":"Bob","vt":"str"},{"t":"cont","v":"Big","vt":"str"},{"t":"eq","v":"K-Rock","vt":"str"},{"t":"eq","v":"CBC","vt":"str"},{"t":"eq","v":"UCB","vt":"str"},{"t":"eq","v":"BBC","vt":"str"},{"t":"eq","v":"BBCWorldService","vt":"str"},{"t":"eq","v":"Capitol","vt":"str"},{"t":"else"}],"checkall":"false","repair":false,"outputs":14,"x":240,"y":380,"wires":[["d21b42df.f524a8"],["4dcef2f9.65868c"],["4007f17f.1de7c"],["26d4b223.86690e"],[],["6e9420876c19c1ef"],["4a671d3f9e4faee3"],["9b1bd608b7c92a5e"],["833ad94c7a889e0e"],["92a87f2c2bbbe18b"],["bbe6b819.f3aeb"],["756e3d04.3d98a4"],["383b54e3.d6649c"],["f6720a92.31c788"]]},{"id":"d21b42df.f524a8","type":"change","z":"96557cc2.7f3b1","name":"Set to The Edge","rules":[{"t":"set","p":"payload","pt":"msg","to":"{\"type\":\"MEDIA\",\"media\":{\"url\":\"http://live.leanstream.co/CILQFM\",\"contentType\":\"audio/mpeg\",\"streamType\":\"LIVE\",\"title\":\"The Edge\",\"image\":\"https://upload.wikimedia.org/wikipedia/en/7/77/102.1_The_Edge.png\"}}","tot":"json"}],"action":"","property":"","from":"","to":"","reg":false,"x":620,"y":40,"wires":[["273a9015b7fb32d1","6523cce0.100de4"]]},{"id":"4dcef2f9.65868c","type":"change","z":"96557cc2.7f3b1","name":"Set to Boom FM","rules":[{"t":"set","p":"payload","pt":"msg","to":"{\"type\":\"MEDIA\",\"media\":{\"url\":\"http://newcap.leanstream.co/CHBMFM\",\"contentType\":\"audio/mpeg\",\"streamType\":\"LIVE\",\"title\":\"Boom FM\",\"image\":\"https://media.socastsrm.com/wordpress/wp-content/blogs.dir/906/files/2017/05/boomlogo-White_Socast3.png\"}}","tot":"json"}],"action":"","property":"","from":"","to":"","reg":false,"x":620,"y":80,"wires":[["273a9015b7fb32d1","6523cce0.100de4"]]},{"id":"6523cce0.100de4","type":"castv2-sender","z":"96557cc2.7f3b1","name":"","connection":"5968e27b13831087","x":960,"y":540,"wires":[[]]},{"id":"4007f17f.1de7c","type":"change","z":"96557cc2.7f3b1","name":"Set to My FM","rules":[{"t":"set","p":"payload","pt":"msg","to":"{\"type\":\"MEDIA\",\"media\":{\"url\":\"http://ice23.securenetsystems.net/CKYM\",\"contentType\":\"audio/mpeg\",\"streamType\":\"LIVE\",\"title\":\"My FM\",\"image\":\"https://upload.wikimedia.org/wikipedia/en/5/56/MyFM.png\"}}","tot":"json"}],"action":"","property":"","from":"","to":"","reg":false,"x":630,"y":120,"wires":[["6523cce0.100de4","273a9015b7fb32d1"]]},{"id":"26d4b223.86690e","type":"change","z":"96557cc2.7f3b1","name":"Set to Q107","rules":[{"t":"set","p":"payload","pt":"msg","to":"{\"type\":\"MEDIA\",\"media\":{\"url\":\"http://live.leanstream.co/CILQFM\",\"contentType\":\"audio/mpeg\",\"streamType\":\"LIVE\",\"title\":\"Q107\",\"image\":\"https://upload.wikimedia.org/wikipedia/en/6/61/CILQ_FM.png\"}}","tot":"json"}],"action":"","property":"","from":"","to":"","reg":false,"x":630,"y":160,"wires":[["6523cce0.100de4","273a9015b7fb32d1"]]},{"id":"bbe6b819.f3aeb","type":"change","z":"96557cc2.7f3b1","name":"Set to BBC Radio 1","rules":[{"t":"set","p":"payload","pt":"msg","to":"{\"type\":\"MEDIA\",\"media\":{\"url\":\"http://www.bbc.co.uk/radio1/\",\"contentType\":\"audio/mpeg\",\"streamType\":\"LIVE\",\"title\":\"BBC Radio 1\",\"image\":\"https://upload.wikimedia.org/wikipedia/commons/thumb/0/0a/BBC_Radio_1.svg/220px-BBC_Radio_1.svg.png\"}}","tot":"json"}],"action":"","property":"","from":"","to":"","reg":false,"x":610,"y":440,"wires":[["6523cce0.100de4","273a9015b7fb32d1"]]},{"id":"756e3d04.3d98a4","type":"change","z":"96557cc2.7f3b1","name":"Set to BBC World Service","rules":[{"t":"set","p":"payload","pt":"msg","to":"{\"type\":\"MEDIA\",\"media\":{\"url\":\"http://bbcwssc.ic.llnwd.net/stream/bbcwssc_mp1_ws-einws\",\"contentType\":\"audio/mpeg\",\"streamType\":\"LIVE\",\"title\":\"BBC World Service\",\"image\":\"https://en.wikipedia.org/wiki/File:BBC_World_Service_red.svg\"}}","tot":"json"}],"action":"","property":"","from":"","to":"","reg":false,"x":590,"y":480,"wires":[["6523cce0.100de4","273a9015b7fb32d1"]]},{"id":"383b54e3.d6649c","type":"change","z":"96557cc2.7f3b1","name":"Set to Capitol FM - London","rules":[{"t":"set","p":"payload","pt":"msg","to":"{\"type\":\"MEDIA\",\"media\":{\"url\":\"http://media-ice.musicradio.com/CapitalMP3\",\"contentType\":\"audio/mpeg\",\"streamType\":\"LIVE\",\"title\":\"Capitol FM London\",\"image\":\"https://upload.wikimedia.org/wikipedia/en/thumb/3/34/Capital_London.png/195px-Capital_London.png\"}}","tot":"json"}],"action":"","property":"","from":"","to":"","reg":false,"x":580,"y":520,"wires":[["6523cce0.100de4","273a9015b7fb32d1"]]},{"id":"42c70a0.b610e78","type":"change","z":"96557cc2.7f3b1","name":"Mute Volume","rules":[{"t":"set","p":"payload","pt":"msg","to":"{\"type\":\"MUTE\"}","tot":"json"}],"action":"","property":"","from":"","to":"","reg":false,"x":510,"y":860,"wires":[["6523cce0.100de4"]]},{"id":"6afbd92d.be5318","type":"change","z":"96557cc2.7f3b1","name":"UnMute Volume","rules":[{"t":"set","p":"payload","pt":"msg","to":"{\"type\":\"UNMUTE\"}","tot":"json"}],"action":"","property":"","from":"","to":"","reg":false,"x":520,"y":900,"wires":[["6523cce0.100de4"]]},{"id":"3f498450.d1f984","type":"comment","z":"96557cc2.7f3b1","name":"Volume Controls","info":"","x":520,"y":820,"wires":[]},{"id":"e1f9f182.7278f8","type":"change","z":"96557cc2.7f3b1","name":"Volume to 10","rules":[{"t":"set","p":"payload","pt":"msg","to":"{\"type\":\"VOLUME\",\"volume\":\"10\"}","tot":"json"}],"action":"","property":"","from":"","to":"","reg":false,"x":510,"y":940,"wires":[["6523cce0.100de4"]]},{"id":"be2acaf4.703cf","type":"switch","z":"96557cc2.7f3b1","name":"Volume Switch","property":"slots.volume","propertyType":"msg","rules":[{"t":"eq","v":"Mute","vt":"str"},{"t":"eq","v":"0","vt":"str"},{"t":"eq","v":"UnMute","vt":"str"},{"t":"eq","v":"1","vt":"str"},{"t":"eq","v":"2","vt":"str"},{"t":"eq","v":"3","vt":"str"},{"t":"eq","v":"4","vt":"str"},{"t":"eq","v":"5","vt":"str"},{"t":"eq","v":"6","vt":"str"},{"t":"eq","v":"7","vt":"str"},{"t":"eq","v":"8","vt":"str"},{"t":"eq","v":"9","vt":"str"},{"t":"eq","v":"10","vt":"str"},{"t":"else"}],"checkall":"false","repair":false,"outputs":14,"x":240,"y":1020,"wires":[["42c70a0.b610e78"],["42c70a0.b610e78"],["6afbd92d.be5318"],["e1f9f182.7278f8"],["5ebe8021.101ff8"],["9ca18661.d144a8"],["b278cd9d.a8f308"],["67206340.d55324"],["34db420.65665be"],["69438a8.7f6d4f4"],["71c0b712.babf28"],["c02f2e1c.bc332"],["45baa218.b975dc"],["e8eb3ccae3875984"]]},{"id":"5ebe8021.101ff8","type":"change","z":"96557cc2.7f3b1","name":"Volume to 20","rules":[{"t":"set","p":"payload","pt":"msg","to":"{\"type\":\"VOLUME\",\"volume\":\"20\"}","tot":"json"}],"action":"","property":"","from":"","to":"","reg":false,"x":510,"y":980,"wires":[["6523cce0.100de4"]]},{"id":"9ca18661.d144a8","type":"change","z":"96557cc2.7f3b1","name":"Volume to 30","rules":[{"t":"set","p":"payload","pt":"msg","to":"{\"type\":\"VOLUME\",\"volume\":\"30\"}","tot":"json"}],"action":"","property":"","from":"","to":"","reg":false,"x":510,"y":1020,"wires":[["6523cce0.100de4"]]},{"id":"b278cd9d.a8f308","type":"change","z":"96557cc2.7f3b1","name":"Volume to 40","rules":[{"t":"set","p":"payload","pt":"msg","to":"{\"type\":\"VOLUME\",\"volume\":\"40\"}","tot":"json"}],"action":"","property":"","from":"","to":"","reg":false,"x":510,"y":1060,"wires":[["6523cce0.100de4"]]},{"id":"67206340.d55324","type":"change","z":"96557cc2.7f3b1","name":"Volume to 50","rules":[{"t":"set","p":"payload","pt":"msg","to":"{\"type\":\"VOLUME\",\"volume\":\"50\"}","tot":"json"}],"action":"","property":"","from":"","to":"","reg":false,"x":510,"y":1100,"wires":[["6523cce0.100de4"]]},{"id":"34db420.65665be","type":"change","z":"96557cc2.7f3b1","name":"Volume to 60","rules":[{"t":"set","p":"payload","pt":"msg","to":"{\"type\":\"VOLUME\",\"volume\":\"60\"}","tot":"json"}],"action":"","property":"","from":"","to":"","reg":false,"x":510,"y":1140,"wires":[["6523cce0.100de4"]]},{"id":"69438a8.7f6d4f4","type":"change","z":"96557cc2.7f3b1","name":"Volume to 70","rules":[{"t":"set","p":"payload","pt":"msg","to":"{\"type\":\"VOLUME\",\"volume\":\"70\"}","tot":"json"}],"action":"","property":"","from":"","to":"","reg":false,"x":510,"y":1180,"wires":[["6523cce0.100de4"]]},{"id":"71c0b712.babf28","type":"change","z":"96557cc2.7f3b1","name":"Volume to 80","rules":[{"t":"set","p":"payload","pt":"msg","to":"{\"type\":\"VOLUME\",\"volume\":\"80\"}","tot":"json"}],"action":"","property":"","from":"","to":"","reg":false,"x":510,"y":1220,"wires":[["6523cce0.100de4"]]},{"id":"c02f2e1c.bc332","type":"change","z":"96557cc2.7f3b1","name":"Volume to 90","rules":[{"t":"set","p":"payload","pt":"msg","to":"{\"type\":\"VOLUME\",\"volume\":\"90\"}","tot":"json"}],"action":"","property":"","from":"","to":"","reg":false,"x":510,"y":1260,"wires":[["6523cce0.100de4"]]},{"id":"45baa218.b975dc","type":"change","z":"96557cc2.7f3b1","name":"Volume to 100","rules":[{"t":"set","p":"payload","pt":"msg","to":"{\"type\":\"VOLUME\",\"volume\":\"100\"}","tot":"json"}],"action":"","property":"","from":"","to":"","reg":false,"x":520,"y":1300,"wires":[["6523cce0.100de4"]]},{"id":"f1dff7dc.7c37c8","type":"comment","z":"96557cc2.7f3b1","name":"Radio Station Streams","info":"","x":400,"y":40,"wires":[]},{"id":"f6720a92.31c788","type":"switch","z":"96557cc2.7f3b1","name":"Media Controls","property":"slots.radiocontrols","propertyType":"msg","rules":[{"t":"eq","v":"play","vt":"str"},{"t":"eq","v":"pause","vt":"str"},{"t":"eq","v":"stop","vt":"str"},{"t":"else"}],"checkall":"true","repair":false,"outputs":4,"x":320,"y":700,"wires":[["c9823812.c0b7f8"],["6ae13e4f.e4bec"],["eb83c091.d5ab"],["be2acaf4.703cf"]]},{"id":"c9823812.c0b7f8","type":"change","z":"96557cc2.7f3b1","name":"Play","rules":[{"t":"set","p":"payload","pt":"msg","to":"{\"type\":\"PLAY\"}","tot":"json"}],"action":"","property":"","from":"","to":"","reg":false,"x":510,"y":660,"wires":[["6523cce0.100de4"]]},{"id":"6ae13e4f.e4bec","type":"change","z":"96557cc2.7f3b1","name":"Pause","rules":[{"t":"set","p":"payload","pt":"msg","to":"{\"type\":\"PAUSE\"}","tot":"json"}],"action":"","property":"","from":"","to":"","reg":false,"x":510,"y":700,"wires":[["6523cce0.100de4"]]},{"id":"eb83c091.d5ab","type":"change","z":"96557cc2.7f3b1","name":"Stop","rules":[{"t":"set","p":"payload","pt":"msg","to":"{\"type\":\"STOP\"}","tot":"json"}],"action":"","property":"","from":"","to":"","reg":false,"x":510,"y":740,"wires":[["6523cce0.100de4"]]},{"id":"2ba679a6.108366","type":"comment","z":"96557cc2.7f3b1","name":"Speaker/Media Controls","info":"","x":520,"y":620,"wires":[]},{"id":"92a87f2c2bbbe18b","type":"change","z":"96557cc2.7f3b1","name":"Set to UCB","rules":[{"t":"set","p":"payload","pt":"msg","to":"{\"type\":\"MEDIA\",\"media\":{\"url\":\"http://s7.viastreaming.net:8600/\",\"contentType\":\"audio/mpeg\",\"streamType\":\"LIVE\",\"title\":\"UCB Radio\",\"image\":\"https://www.ucbcanada.com/uploads/seo/logo.png\"}}","tot":"json"}],"action":"","property":"","from":"","to":"","reg":false,"x":630,"y":400,"wires":[["6523cce0.100de4","273a9015b7fb32d1"]]},{"id":"833ad94c7a889e0e","type":"change","z":"96557cc2.7f3b1","name":"Set to CBC","rules":[{"t":"set","p":"payload","pt":"msg","to":"{\"type\":\"MEDIA\",\"media\":{\"url\":\"https://cbcliveradio-lh.akamaihd.net/i/CBCR1_OTT@348188/master.m3u8\",\"contentType\":\"audio/mpeg\",\"streamType\":\"LIVE\",\"title\":\"CBC Radio 1\",\"image\":\"https://upload.wikimedia.org/wikipedia/commons/thumb/e/ee/CBC_Radio_Logo.svg/220px-CBC_Radio_Logo.svg.png\"}}","tot":"json"}],"action":"","property":"","from":"","to":"","reg":false,"x":630,"y":360,"wires":[["6523cce0.100de4","273a9015b7fb32d1"]]},{"id":"9b1bd608b7c92a5e","type":"change","z":"96557cc2.7f3b1","name":"Set to K-Rock","rules":[{"t":"set","p":"payload","pt":"msg","to":"{\"type\":\"MEDIA\",\"media\":{\"url\":\"https://rogers-hls.leanstream.co/rogers/kin1057.stream/48k/playlist.m3u8\",\"contentType\":\"audio/mpeg\",\"streamType\":\"LIVE\",\"title\":\"BBC Radio 1\",\"image\":\"https://upload.wikimedia.org/wikipedia/en/thumb/e/e5/K-Rock_105.7_logo.svg/150px-K-Rock_105.7_logo.svg.png\"}}","tot":"json"}],"action":"","property":"","from":"","to":"","reg":false,"x":620,"y":320,"wires":[["6523cce0.100de4","273a9015b7fb32d1"]]},{"id":"4a671d3f9e4faee3","type":"change","z":"96557cc2.7f3b1","name":"Set to Big FM","rules":[{"t":"set","p":"payload","pt":"msg","to":"{\"type\":\"MEDIA\",\"media\":{\"url\":\"http://live.leanstream.co/CFMKFM-MP3\",\"contentType\":\"audio/mpeg\",\"streamType\":\"LIVE\",\"title\":\"Big FM\",\"image\":\"https://upload.wikimedia.org/wikipedia/en/thumb/1/1a/CFMK_BIG96.3_logo.png/250px-CFMK_BIG96.3_logo.png\"}}","tot":"json"}],"action":"","property":"","from":"","to":"","reg":false,"x":620,"y":280,"wires":[["6523cce0.100de4","273a9015b7fb32d1"]]},{"id":"6e9420876c19c1ef","type":"change","z":"96557cc2.7f3b1","name":"Set to Bob FM","rules":[{"t":"set","p":"payload","pt":"msg","to":"{\"type\":\"MEDIA\",\"media\":{\"url\":\"https://22073.live.streamtheworld.com/CJPTFMAAC.aac\",\"contentType\":\"audio/mpeg\",\"streamType\":\"LIVE\",\"title\":\"Bob FM\",\"image\":\"https://upload.wikimedia.org/wikipedia/commons/thumb/3/35/Bob-FM-Brockville.png/150px-Bob-FM-Brockville.png\"}}","tot":"json"}],"action":"","property":"","from":"","to":"","reg":false,"x":620,"y":240,"wires":[["6523cce0.100de4","273a9015b7fb32d1"]]},{"id":"8a328aded6da79a7","type":"change","z":"96557cc2.7f3b1","name":"Set to Fresh","rules":[{"t":"set","p":"payload","pt":"msg","to":"{\"type\":\"MEDIA\",\"media\":{\"url\":\"http://live.leanstream.co/CKWSFM?args=web_01\",\"contentType\":\"audio/mpeg\",\"streamType\":\"LIVE\",\"title\":\"Fresh Radio\",\"image\":\"https://upload.wikimedia.org/wikipedia/en/5/5f/CKWS_freshRadio104.3_logo.png\"}}","tot":"json"}],"action":"","property":"","from":"","to":"","reg":false,"x":630,"y":200,"wires":[["6523cce0.100de4","273a9015b7fb32d1"]]},{"id":"be3d7e677a912931","type":"debug","z":"96557cc2.7f3b1","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":370,"y":580,"wires":[]},{"id":"e9dccaa707f0e967","type":"function","z":"96557cc2.7f3b1","name":"Q107 fix","func":"if (msg.slots.station) {\n    msg.slots.station = msg.slots.station.replace(\"Q One O Seven\", \"Q107\");\n}\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":120,"y":540,"wires":[["3ffcf753.a145a"]]},{"id":"c855eb911347c7f9","type":"link in","z":"96557cc2.7f3b1","name":"In from Flow 1","links":["8518cd2330e9e245"],"x":155,"y":40,"wires":[["e9dccaa707f0e967"]]},{"id":"fe2707e7dec3cff3","type":"comment","z":"96557cc2.7f3b1","name":"Send Speech text back to Flow 1 for output","info":"","x":1120,"y":200,"wires":[]},{"id":"e8eb3ccae3875984","type":"link out","z":"96557cc2.7f3b1","name":"Unknown Request Out","mode":"link","links":["7ade62dd838b317b"],"x":455,"y":1420,"wires":[]},{"id":"450bcacbbc31680a","type":"comment","z":"96557cc2.7f3b1","name":"Unknown Sending to Good Morning","info":"","x":580,"y":1360,"wires":[]},{"id":"273a9015b7fb32d1","type":"function","z":"96557cc2.7f3b1","name":"Random Station Name Speech","func":"const station = msg.slots.station;\nconst options = [\n    `Now playing ${station}`,\n    `Playing ${station}`,\n    `Tuning to ${station}`,\n    `${station}, great choice`,\n    //add more here\n];\nconst index = Math.floor(Math.random() * options.length);\nmsg.payload = options[index];\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1090,"y":260,"wires":[["7b69d47055c23024"]]},{"id":"7b69d47055c23024","type":"link out","z":"96557cc2.7f3b1","name":"Station Name Test","mode":"link","links":["fdf0440997c3a4ea"],"x":1285,"y":260,"wires":[]},{"id":"5968e27b13831087","type":"castv2-connection","name":"","target":"Speakers speaker","host":"","port":"8009"}]

Again I know there are efficiencies to find, the Q107 fix node comes to mind.

1 Like

@rlongfield I was wondering if I could pick your brain a little bit?

At the moment I’m trying to turn on a simple light group office_lights(made from office_light1 and office_light2). Rhasspy recognises turn off office lights command and passes through to node red but obviously home assistant needs it to be office_lights so I modified one of your function nodes to include only the following

if (msg.slots.object) {
msg.slots.object = msg.slots.object.replace(" ", “_”);
}
return msg;

But as far as I can see its not changing the output to the correct format

I think you want
msg.slots.object = msg.slots.name.replace(" ", “_”);

More accurately
if (msg.slots.name) {
msg.slots.object = msg.slots.name.replace(" ", “_”);
}
return msg;

I think it might be:

if (msg.slots.name) {
    msg.slots.name = msg.slots.name.replace(" ", "_");
}
return msg;

You’re right. I missed changing one reference of object to name

@jrb5665 @rlongfield Thanks so much for your replies guys (not gender specific). I’ll give it a go this weekend

I once sat and re-read pages of code trying to find the error in the student lounge. A friend of mine who was in graphic arts grabbed my printout and said ‘let me see that’. After a few minutes she said ‘How come there isn’t a comma here?’

I learned a second set of eyes is super important, it’s always the simple things that cause so many headaches :slight_smile:

I’m glad I was able to help instead of always asking for help.

So, did it work out?

Sorry for the delay, had a family medical situation.

Here is the third flow. This one started as a ‘Good Morning’ flow in which Rhasspy responded with 'Good Morning, it’s here the weather for .

The date and time are pulled from Node Red functions and the local weather is pulled from Home Assistant using the Environment Canada integration which pulls the text of the local weather. This information is combined and sent to Rhasspy to speak.

Here is a screenshot of the flow:

Here is the flow code:

[{"id":"7ade62dd838b317b","type":"link in","z":"0c02c9336ac1f198","name":"Good Morning","links":["e8eb3ccae3875984"],"x":45,"y":300,"wires":[["bda376b2dfecc097"]]},{"id":"bda376b2dfecc097","type":"switch","z":"0c02c9336ac1f198","name":"","property":"intent.name","propertyType":"msg","rules":[{"t":"eq","v":"GoodMorning","vt":"str"},{"t":"eq","v":"GoodAfternoon","vt":"str"},{"t":"eq","v":"GoodEvening","vt":"str"},{"t":"else"}],"checkall":"true","repair":false,"outputs":4,"x":160,"y":300,"wires":[["7246466b63027090"],["7246466b63027090"],["7246466b63027090"],["1c8d9c1c4fae7fbb"]]},{"id":"1c8d9c1c4fae7fbb","type":"link out","z":"0c02c9336ac1f198","name":"Unknown Request Out","mode":"link","links":["cec7f37cee58adda"],"x":275,"y":480,"wires":[]},{"id":"81e09a621debb0a7","type":"comment","z":"0c02c9336ac1f198","name":"Unknown Sending to News and Jokes","info":"","x":310,"y":440,"wires":[]},{"id":"7246466b63027090","type":"time-range-switch","z":"0c02c9336ac1f198","name":"Morning","lat":"","lon":"","startTime":"00:00","endTime":"11:59","startOffset":0,"endOffset":0,"x":380,"y":140,"wires":[["08c6bc03df8fdc50"],["f4269ccb40969c6f"]]},{"id":"634b90691a4532e3","type":"time-range-switch","z":"0c02c9336ac1f198","name":"Evening","lat":"","lon":"","startTime":"16:00","endTime":"23:30","startOffset":0,"endOffset":0,"x":380,"y":260,"wires":[["51bd321942fd8a0a"],[]]},{"id":"f4269ccb40969c6f","type":"time-range-switch","z":"0c02c9336ac1f198","name":"Afternoon","lat":"","lon":"","startTime":"12:00","endTime":"15:59","startOffset":0,"endOffset":0,"x":380,"y":200,"wires":[["56ed3d23e4d0c483"],["634b90691a4532e3"]]},{"id":"08c6bc03df8fdc50","type":"change","z":"0c02c9336ac1f198","name":"Good Morning","rules":[{"t":"set","p":"slots.am-pm","pt":"msg","to":"Good Morning","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":560,"y":120,"wires":[["57ce8155a9b3ed3a"]]},{"id":"4c47ea676720fdbf","type":"template","z":"0c02c9336ac1f198","name":"Good Morning","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"{{slots.am-pm}}, it is {{slots.time}} on {{slots.date}}. The Environment Canada forecast for {{{slots.weather}}}","output":"str","x":840,"y":260,"wires":[["c2d1bddbe1d79c3e"]]},{"id":"5c885d384d08a1c2","type":"api-current-state","z":"0c02c9336ac1f198","name":"","server":"890f6b21.aaf278","version":2,"outputs":1,"halt_if":"","halt_if_type":"str","halt_if_compare":"is_not","entity_id":"sensor.forecast","state_type":"str","blockInputOverrides":false,"outputProperties":[{"property":"slots.weather","propertyType":"msg","value":"","valueType":"entityState"}],"override_topic":false,"state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","x":890,"y":180,"wires":[["4c47ea676720fdbf"]]},{"id":"c2d1bddbe1d79c3e","type":"link out","z":"0c02c9336ac1f198","name":"Good Morning Speech Out","mode":"link","links":["fdf0440997c3a4ea"],"x":775,"y":340,"wires":[]},{"id":"2501c141dbe7fecf","type":"function","z":"0c02c9336ac1f198","name":"Get Time","func":"var timeString = new Date().toLocaleTimeString([],\n{\n    hour: \"2-digit\", \n    minute: \"2-digit\",\n    hour12: true\n})\n\n{\nmsg.slots.time = timeString;\n}\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":960,"y":120,"wires":[["5c885d384d08a1c2"]]},{"id":"56ed3d23e4d0c483","type":"change","z":"0c02c9336ac1f198","name":"Good Afternoon","rules":[{"t":"set","p":"slots.am-pm","pt":"msg","to":"Good Afternoon","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":560,"y":180,"wires":[["57ce8155a9b3ed3a"]]},{"id":"51bd321942fd8a0a","type":"change","z":"0c02c9336ac1f198","name":"Good Evening","rules":[{"t":"set","p":"slots.am-pm","pt":"msg","to":"Good Evening","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":560,"y":240,"wires":[["57ce8155a9b3ed3a"]]},{"id":"57ce8155a9b3ed3a","type":"change","z":"0c02c9336ac1f198","name":"Get Date","rules":[{"t":"set","p":"slots.date","pt":"msg","to":"$moment().format('ddd MMM DD')","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":760,"y":120,"wires":[["2501c141dbe7fecf"]]},{"id":"890f6b21.aaf278","type":"server","name":"Home Assistant","version":2,"addon":false,"rejectUnauthorizedCerts":true,"ha_boolean":"y|yes|true|on|home|open","connectionDelay":true,"cacheJson":true,"heartbeat":false,"heartbeatInterval":"30"}]

Here is my fourth and (currently) final flow. It is the News and Joke flow. So for each flow there is a switch that takes the intent from Rhasspy and checks to see if it has a switch option for that, if not I use a ‘link out’ node to pass it to the next flow that receives the intent using a ‘link in’ node. Since this is the last flow in the series there isn’t a link out, instead it has a ‘Unknown Request’. This is just a ‘template’ node that say’s “I’m sorry I didn’t understand your request” and sends that off to Rhasspy to speak.
This was the best solution I could come up with to keep the flows as uncluttered as possible and to make it easy to expand the sequence. If I want to add a new flow I just need to move the ‘Unknown Request’ node to the new flow and replace it with a ‘link out’ node.

In this node I’m grabbing the CBC Hourly News mp3 file using a ‘http request node’. I am then passing that .mp3 to a ‘ffmpeg conversion’ node which I have set to convert the mp3 to a mono .wav file. You can get the ‘ffmpeg conversion’ node using the ‘node-red-contrib-media-utils’ add-on.
The .wav is then passed to a ‘function node’ which sets the headers, followed by a delay of 5 secs. I don’t know if the delay is required but I thought I’d bake in a bit of a buffer just in case. After the delay the .wav file is sent to Rhasspy using a ‘http request’ node to the Rhasspy /api/play-wav function.

For the Joke section I’m tapping into the icanhazdadjoke.com website. The only request the have is that when asking for a joke that the User-Agent portion of the http request be set to something that would allow them to see who/what is using the jokes. I’m accomplishing this using a ‘Change node’ to set the User-Agent in the headers to ‘Rhasspy Skill’. After setting the header I user a ‘http request node’ to get a parsed JSON object. Last step is to use a ‘template node’ to pull just the joke out of the payload and pass it along.

This was by far the hardest flow to create as it required a lot of testing to figure out how to get, and then process the information I wanted.

Here is a screenshot of the flow:

Here is the flow code:

[{"id":"3030ec117ce518a1","type":"tab","label":"News and Jokes","disabled":false,"info":"","env":[]},{"id":"fffb8cd8ef850ab4","type":"debug","z":"3030ec117ce518a1","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":970,"y":600,"wires":[]},{"id":"7b4a4df181fa98ef","type":"ffmpeg-conversion","z":"3030ec117ce518a1","name":"MP3 to Wav Conversion","format":"wav","audiochannels":"mono","x":370,"y":260,"wires":[["3fff6d1b7d74f31a"]]},{"id":"3fff6d1b7d74f31a","type":"function","z":"3030ec117ce518a1","name":"Setting Message Headers","func":"msg.headers = {};\nmsg.headers['Content-Type'] = 'audio/wav';\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":650,"y":260,"wires":[["779ed042f36fde8d"]]},{"id":"6d210ba2f4ec1dcc","type":"http request","z":"3030ec117ce518a1","name":"Get CBC Hourly News","method":"GET","ret":"bin","paytoqs":"ignore","url":"http://podcast-a.akamaihd.net/mp3/hourlynews.mp3","tls":"","persist":false,"proxy":"","authType":"","senderr":false,"credentials":{},"x":360,"y":200,"wires":[["7b4a4df181fa98ef","cd0d4fb0f234acba"]]},{"id":"1759cb7d163cccd2","type":"http request","z":"3030ec117ce518a1","name":"","method":"POST","ret":"txt","paytoqs":"ignore","url":"http://192.168.1.230:12101/api/play-wav","tls":"","persist":false,"proxy":"","authType":"","senderr":false,"credentials":{},"x":710,"y":340,"wires":[[]]},{"id":"cd0d4fb0f234acba","type":"template","z":"3030ec117ce518a1","name":"","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"One moment while I fetch the news","output":"str","x":620,"y":200,"wires":[["885921b779d12688"]]},{"id":"885921b779d12688","type":"link out","z":"3030ec117ce518a1","name":"News Speech","mode":"link","links":["fdf0440997c3a4ea"],"x":995,"y":340,"wires":[]},{"id":"779ed042f36fde8d","type":"delay","z":"3030ec117ce518a1","name":"","pauseType":"delay","timeout":"5","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"allowrate":false,"outputs":1,"x":480,"y":340,"wires":[["1759cb7d163cccd2"]]},{"id":"ccb4bde7dbeb0aa7","type":"switch","z":"3030ec117ce518a1","name":"","property":"intent.name","propertyType":"msg","rules":[{"t":"eq","v":"News","vt":"str"},{"t":"eq","v":"Joke","vt":"str"},{"t":"else"}],"checkall":"true","repair":false,"outputs":3,"x":130,"y":260,"wires":[["6d210ba2f4ec1dcc"],["24e196527a2e1d55"],["e23d5444a3db2f0c"]]},{"id":"cec7f37cee58adda","type":"link in","z":"3030ec117ce518a1","name":"News and Jokes","links":["1c8d9c1c4fae7fbb"],"x":35,"y":260,"wires":[["ccb4bde7dbeb0aa7"]]},{"id":"24e196527a2e1d55","type":"change","z":"3030ec117ce518a1","name":"","rules":[{"t":"set","p":"headers","pt":"msg","to":"{\"Accept\":\"application/json\",\"User-Agent\":\"Rhasspy Skill\"}","tot":"json"}],"action":"","property":"","from":"","to":"","reg":false,"x":350,"y":460,"wires":[["d16702df4d092ae5"]]},{"id":"d16702df4d092ae5","type":"http request","z":"3030ec117ce518a1","name":"Dad Jokes","method":"GET","ret":"obj","paytoqs":"ignore","url":"https://icanhazdadjoke.com/","tls":"","persist":false,"proxy":"","authType":"","senderr":true,"credentials":{},"x":570,"y":460,"wires":[["0ec027152009c054"]]},{"id":"0ec027152009c054","type":"template","z":"3030ec117ce518a1","name":"","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"{{payload.joke}} !","output":"str","x":800,"y":520,"wires":[["885921b779d12688","fffb8cd8ef850ab4"]]},{"id":"68775c9835fbb16f","type":"comment","z":"3030ec117ce518a1","name":"Speech out to Flow 1","info":"","x":1020,"y":260,"wires":[]},{"id":"e23d5444a3db2f0c","type":"template","z":"3030ec117ce518a1","name":"Unknown Request","field":"payload","fieldType":"msg","format":"handlebars","syntax":"plain","template":"I'm Sorry I did not understand your request.","output":"str","x":330,"y":560,"wires":[["885921b779d12688"]]},{"id":"3cebcbdf801d174d","type":"comment","z":"3030ec117ce518a1","name":"Get Random Joke","info":"","x":470,"y":400,"wires":[]}]

The only issue I have with this is that I’m using NanoTTS for Rhasspy’s voice and it makes understanding the jokes difficult at times as there is some buzz generated in the speaker.

2 Likes

Hi @rlongfield!
Amazing flow! I would love to copy it to hear the (most recent) news from german radio Deutschlandfunk. My problem is I can not find a link that points to the most recent mp3. I only find xml, rss or html links.

https://www.deutschlandfunk.de/nachrichten-108.xml
or
https://www.deutschlandfunk.de/nachrichten-100.rss
for example.

Do you have a tip for me how to solve this?

Thank you! :slight_smile:

So I noticed on this page there is a link to the latest mp3 file.
Nachrichten vom 08.02.2022, 19:00 Uhr →MP3

The →MP3 portion above links out to the actual MP3 file. https://podcast-mp3.dradio.de/podcast/2022/02/08/nachrichten_dlf_20220208_1900_5a7e09eb.mp3

So the difference between your news source and mine is that the CBC has a static file name for the MP3 file so I don’t need to do anything fancy and I can just code NodeRed to grab the one file regardless of the date or time.

Your source seems to update not just the file name but also the file location (sub directories). They also seem to be adding a random string of characters to the end of the file name. Perhaps it’s a hash of some sort?
If it wasn’t for that string it would take some work but it would be possible to create a NodeRed flow that would grab the most current MP3 file and play it. Unless you know what that end string is or figure out what it might be I think you are out of luck.

I recommend you head over to the NodeRed forum, there are some pretty smart people there so there might be a different way of grabbing the link to the most current file that I don’t know about. Perhaps a way to scrape the xml file?

Sorry I couldn’t be of more help.

1 Like

Thank you very much @rlongfield! Your thoughts helped me a lot!

After your answer I stumbled upon a solution via Home Assistant …and the next problem.

So far I could extract the link to the newest mp3-file via a Home Assistant Sensor,
in my config.yaml it looks like this:

> sensor:
>   - platform: scrape
>     resource: https://www.deutschlandfunk.de/podcast-nachrichten.1257.de.podcast.xml
>     name: podcast_deutschlandfunk_nachrichten
>     select: 'enclosure:nth-of-type(1)'
>     attribute: url
>     scan_interval: 300

Then I am adding one Home Assitant Node in your flow, which checks the current state of sensor.podcast_deutschlandfunk_nachrichten and passes it in your flow.

And everything seems to work until the conversion node. It is stuck with “reading buffer”. Any idea why?

Hmmm there isn’t much to change in the conversion node.
What is the MP3 file your sensor is returning? Can you provide an example and I’ll see what results I get?

1 Like