And you also added Note the keywords “intent:” and “intent_script:” to HA configuration.yaml ? BOTH are required.
I chose to place the actual intents in a separate include file to make them easier to edit; but you can simply list the intents directly after “intent_script:”.
intent:
intent_script: !include intents.yaml
Beyond that, I’m not sure what can go wrong. There may be something in one of the various log files, but I’m not sure which one to look in.
If you are interested in the NR route, I recommend Paul Romkes’ rhasspy wiki Intent Handling with HA and Node Red tutorial,
Configuring the MQTT In node is straightforward
My Rhasspy flow looks complicated because my Kitchen and Living_room satellites often hear (and try to process) the same command simultaneously … resulting in me using session variables to check if the intent being processed is the same one processed <3 seconds ago. Added to that, there are plenty of false alarms from the TV
[{"id":"13b29f95.47317","type":"switch","z":"b74f060c.6b2908","name":"find intent","property":"payload.intent.intentName","propertyType":"msg","rules":[{"t":"eq","v":"WatchTvChannel","vt":"str"},{"t":"eq","v":"WatchProgramme","vt":"str"},{"t":"eq","v":"GoodMorning","vt":"str"},{"t":"eq","v":"Dinnertime","vt":"str"},{"t":"eq","v":"GetTemperature","vt":"str"},{"t":"eq","v":"WeatherCurrent","vt":"str"},{"t":"eq","v":"WeatherForecast","vt":"str"},{"t":"eq","v":"Movietime","vt":"str"},{"t":"eq","v":"GetTime","vt":"str"},{"t":"eq","v":"TVoff","vt":"str"},{"t":"eq","v":"GoodNight","vt":"str"},{"t":"eq","v":"Play","vt":"str"},{"t":"eq","v":"TimerSet","vt":"str"},{"t":"eq","v":"TimerCancel","vt":"str"},{"t":"eq","v":"ChangeSwitchState","vt":"str"},{"t":"eq","v":"ChangeCoverState","vt":"str"},{"t":"eq","v":"ChangeCoverLevel","vt":"str"},{"t":"eq","v":"ChangeLightState","vt":"str"},{"t":"eq","v":"ChangeLightBrightness","vt":"str"},{"t":"eq","v":"XMASLights","vt":"str"},{"t":"else"}],"checkall":"false","repair":false,"outputs":21,"x":100,"y":720,"wires":[["eb98925768dc9495"],["8aa8328b2b29c9e5"],["f67084dc288b4bc3"],["3714768a7b16ba91"],["cbe27e388ccedef7"],["e337cc3fb23a3756"],["3586706ae38d0dcd"],["e0e58732b65afd0d"],["79279baa.e9e9d4"],["802d9c301fe10e25"],["2edc03f6e9bd21e2"],["4a57d326c3aa8048"],["205297a969810602"],["45aadc897818c429"],["c25a5fe4915e4c80"],["e6889705dcae7372"],[],["5d0b70fd3a9f7a96"],["497278a27faff99c"],["0fdb581e2d9c6372"],["f058b4b213fc15b6"]]},{"id":"79279baa.e9e9d4","type":"function","z":"b74f060c.6b2908","name":"GetTime","func":"\n\n// Extract Hours and Minutes \nvar date = new Date();\nvar timeH = date.getHours();\nif (timeH > 12) { timeH -= 12 } // afternoon to 12-hour clock\nvar timeM = date.getMinutes();\nnode.warn(\"Hours=\" + timeH +\", Min=\"+ timeM);\n\n// say a leading \"oh\" if less than 10 minutes past the hour\nvar timeStringM = timeM;\nif (timeM == 0) { \n timeStringM = \"oh clock\" \n} else if (timeM < 10) { \n timeStringM = \"oh \" + timeM \n};\n\nmsg.payload = \"The time is \" + timeH + \" \" + timeStringM;\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":380,"y":640,"wires":[["ada61f79f15bb868"]]},{"id":"f67084dc288b4bc3","type":"link call","z":"b74f060c.6b2908","name":"","links":["620d653529fbe536"],"timeout":"30","x":420,"y":400,"wires":[["ada61f79f15bb868"]]},{"id":"2edc03f6e9bd21e2","type":"link call","z":"b74f060c.6b2908","name":"","links":["4963009a35c24980"],"linkType":"static","timeout":"600","x":410,"y":720,"wires":[["ada61f79f15bb868"]]},{"id":"e0e58732b65afd0d","type":"link call","z":"b74f060c.6b2908","name":"","links":["ea8c42aa5725fb01"],"timeout":"30","x":410,"y":600,"wires":[["ada61f79f15bb868"]]},{"id":"3714768a7b16ba91","type":"link call","z":"b74f060c.6b2908","name":"","links":["5227b56088071a3f"],"timeout":"30","x":410,"y":440,"wires":[["ada61f79f15bb868"]]},{"id":"e337cc3fb23a3756","type":"link call","z":"b74f060c.6b2908","name":"","links":["90eb5fa0d241d6e7"],"timeout":"30","x":400,"y":520,"wires":[["ada61f79f15bb868"]]},{"id":"cbe27e388ccedef7","type":"link call","z":"b74f060c.6b2908","name":"","links":["efeff2c75b6558b7"],"timeout":"30","x":400,"y":480,"wires":[["ada61f79f15bb868"]]},{"id":"3586706ae38d0dcd","type":"link call","z":"b74f060c.6b2908","name":"","links":["0434d546133de08e"],"timeout":"30","x":410,"y":560,"wires":[["ada61f79f15bb868"]]},{"id":"4a57d326c3aa8048","type":"link call","z":"b74f060c.6b2908","name":"","links":["0758a72b677e6fd7"],"timeout":"30","x":410,"y":760,"wires":[["ada61f79f15bb868"]]},{"id":"eb98925768dc9495","type":"change","z":"b74f060c.6b2908","name":"tidy TV channel name","rules":[{"t":"change","p":"slots.channel","pt":"msg","from":"seven","fromt":"str","to":"TV channel 7","tot":"str"},{"t":"change","p":"slots.channel","pt":"msg","from":"nine","fromt":"str","to":"TV 9Now","tot":"str"},{"t":"change","p":"slots.channel","pt":"msg","from":"ten","fromt":"str","to":"TV 10play","tot":"str"},{"t":"change","p":"slots.channel","pt":"msg","from":"abc","fromt":"str","to":"TV ABC","tot":"str"},{"t":"change","p":"slots.channel","pt":"msg","from":"sbs","fromt":"str","to":"TV SBS","tot":"str"},{"t":"change","p":"slots.channel","pt":"msg","from":"rai","fromt":"str","to":"TV RAI Italy","tot":"str"},{"t":"change","p":"slots.channel","pt":"msg","from":"stan","fromt":"str","to":"STAN","tot":"str"},{"t":"change","p":"slots.channel","pt":"msg","from":"ted","fromt":"str","to":"TED","tot":"str"},{"t":"change","p":"slots.channel","pt":"msg","from":"prime","fromt":"str","to":"Amazon Prime","tot":"str"},{"t":"change","p":"slots.channel","pt":"msg","from":"binge","fromt":"str","to":"BINGE","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":420,"y":320,"wires":[["ded5b9e04e3d4e72"]]},{"id":"ded5b9e04e3d4e72","type":"link call","z":"b74f060c.6b2908","name":"","links":["43ee774c3579cae2"],"timeout":"90","x":660,"y":320,"wires":[["ada61f79f15bb868"]]},{"id":"8aa8328b2b29c9e5","type":"link call","z":"b74f060c.6b2908","name":"","links":["2672aeb014579af3"],"timeout":"90","x":430,"y":360,"wires":[["ada61f79f15bb868"]]},{"id":"58b18b2324312465","type":"comment","z":"b74f060c.6b2908","name":"process all voice commands","info":"Rhasspy is used on the several satellite machines to parse voice commands, which are then sent here. \n\nVoice commands are defined in \"sentences.ini\" on the base Rhasspy machine (http://homeassistant.local:12101/sentences). Valid commands are called \"intents\". \n\nRhasspy uses MQTT to communicate between modules running on Base and Satellite machines. Node-RED's \"websocket intent listener\" listens to the intents ... and this is where we process them. \n","x":140,"y":40,"wires":[]},{"id":"de42b45806fed3ec","type":"comment","z":"b74f060c.6b2908","name":"Check for multiple satellies responding","info":"because of open plan layout, often the Kitchen and Living room satellites both respond to the same voice command.\n\nSuggestion at https://laurentchervet.wordpress.com/2019/03/06/satellites-and-the-multidetection-hell/ seems to be to assume the first satellite to respond is the one which is closest (because sound takes time to travel); and so simply take the first command that arrives, and ignore any other sites which also respond within 0.3 seconds. This doesn't seem to allow for the RasPi Zero's slower processor - I will have to do some tests. \nHis solution is basically to set a global variable when the first response arrives, and for each incoming intent to be cheched against this global variable. \n\nEasily resolved using the optional node-red-contrib-semaphore \n\nI want to unlock the flag immediately on ending the Rhasspy session - but I also need the session to time out if there was an error which prevented getting to the endSession !\n","x":180,"y":140,"wires":[]},{"id":"cfa38da8e49eb8ce","type":"mqtt in","z":"b74f060c.6b2908","name":"","topic":"hermes/intent/#","qos":"2","datatype":"json","broker":"3e24a26f3fe6c572","nl":false,"rap":true,"rh":0,"inputs":0,"x":100,"y":80,"wires":[["067b4454e5a11716","fd7c2510f82f999a"]]},{"id":"067b4454e5a11716","type":"debug","z":"b74f060c.6b2908","name":"MQTT intent listener","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":380,"y":80,"wires":[]},{"id":"ab75bd4c6654464d","type":"mqtt out","z":"b74f060c.6b2908","name":"","topic":"hermes/dialogueManager/endSession","qos":"","retain":"","respTopic":"","contentType":"","userProps":"","correl":"","expiry":"","broker":"3e24a26f3fe6c572","x":1170,"y":660,"wires":[]},{"id":"ada61f79f15bb868","type":"template","z":"b74f060c.6b2908","name":"End Session","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"{\n \"sessionId\": \"{{sessionId}}\",\n \"siteId\": \"{{siteId}}\",\n \"text\": \"{{payload}}\"\n}","output":"json","x":830,"y":600,"wires":[["42726d46b392562f","dc78817c08e95239"]]},{"id":"42726d46b392562f","type":"debug","z":"b74f060c.6b2908","name":"end-session","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":1070,"y":720,"wires":[]},{"id":"5a74138a02d2baa2","type":"debug","z":"b74f060c.6b2908","name":"globals saved","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":980,"y":260,"wires":[]},{"id":"126e7c57a0e452d5","type":"debug","z":"b74f060c.6b2908","name":"ERROR: INTENT NOT RECOGNISED","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":810,"y":1120,"wires":[]},{"id":"e7354935ba862272","type":"inject","z":"b74f060c.6b2908","name":"In case of rhasspy non-respond","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":930,"y":480,"wires":[["dc78817c08e95239"]]},{"id":"725a6649afe169ea","type":"switch","z":"b74f060c.6b2908","name":"already inSession","property":"inSession","propertyType":"msg","rules":[{"t":"true"},{"t":"else"}],"checkall":"false","repair":false,"outputs":2,"x":730,"y":200,"wires":[["1aa6b7d178818ef4","08f27e5ca822e8de"],["5a74138a02d2baa2","c4b70ab633dca027"]]},{"id":"08f27e5ca822e8de","type":"mqtt out","z":"b74f060c.6b2908","name":"","topic":"hermes/dialogueManager/endSession","qos":"","retain":"","respTopic":"","contentType":"","userProps":"","correl":"","expiry":"","broker":"3e24a26f3fe6c572","x":1030,"y":200,"wires":[]},{"id":"dc78817c08e95239","type":"change","z":"b74f060c.6b2908","name":"reset global.inSession","rules":[{"t":"set","p":"inSession","pt":"global","to":"false","tot":"bool"}],"action":"","property":"","from":"","to":"","reg":false,"x":1090,"y":600,"wires":[["ab75bd4c6654464d"]]},{"id":"06b6f9593ee21ca6","type":"comment","z":"b74f060c.6b2908","name":"Ignore the secondary satellite","info":"This is the situation where another satellite is already processing a spoken request - most paobably they both heard the same verbal command, so we only want to process the first one ","x":960,"y":120,"wires":[]},{"id":"1aa6b7d178818ef4","type":"debug","z":"b74f060c.6b2908","name":"ignoring secondary satellite","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":1000,"y":160,"wires":[]},{"id":"fd7c2510f82f999a","type":"function","z":"b74f060c.6b2908","name":"Check session and extract all slots","func":"/*\nSometimes the Living room and Kitchen Rhasspy satellites \nboth respond to the same command, almost simultaneously. \n\nIn theory the closest should respond first because speed of \nsound, and the other should be ignored. However: \n (a) RasPi Zero has a slower processor\n (b) could be that a totally different comand is given elsewhere\n (c) assume Node-RED is multi-thread, so could be processing \n commands simultaneously (though on my RasPi4 this does not \n seem to be the case)\n \ncheck for a second Rhasspy satelite responding within 3 seconds \n with the same intent - but maybe misheard\n\nProcedure:\n - check whether received within 3 seconds of last intent. \n - check for low voice recognition confidence\n - if not, record details of the current session and intent to compare against \n - start other processing\n\nVariables in the global scope\n - lastSession.time datetime that last session started\n - lastSession.intentName what intent is being processed\n Note: It appears that we have to use set and get for global variables\n\nReturn msg.inSession is true if the current process should be abandoned\n\n*/\n\n// what if Rhasspy doesn't have much confidence in the command ?\n// probably because it mis-heard\n// Check it now, so msg.intent.name stores what WAS heard, in case we want it later\n\nif (msg.payload.asrConfidence < 0.4) {\n msg.payload = '{ \"sessionId\": \"' + msg.payload.sessionId + '\", \"siteId\": \"' + msg.payload.siteId + '\", \"text\": \"I didnt hear that clearly\"}';\n msg.inSession = true;\n return msg;\n}\n\n\n// Node-RED seems single thread, so we are not currently processing another intent.\n// but maybe we only just finished process the last command ?\n\nlet currTime = Date.now();\nlet lastSession = global.get(\"lastSession\");\n// node.warn(\"lastSession.intentName=\"+ lastSession.intentName +\", lastSession.time=\"+ lastSession.time );\n\nvar seconds = Math.abs(currTime - lastSession.time) / 1000;\n// node.warn(\"last intent received \"+ seconds +\" seconds ago.\");\nif (seconds < 3) {\n // less than 3 seconds - assume the same intent heard (or mis-heard)\n // by another satellite\n msg.payload = '{ \"sessionId\": \"' + msg.payload.sessionId + '\", \"siteId\": \"' + msg.payload.siteId + '\", \"text\": \"\"}'; // no voice response\n msg.inSession = true;\n return msg;\n}\n\n//\n// Not already processing another session ... so start process this one\n//\n\n// save the variables used to determine current session\nlastSession = {time: currTime, sessionId: msg.payload.sessionId, intentName: msg.payload.rawInput, confidence: msg.payload.asrConfidence };\nglobal.set(\"lastSession\", lastSession);\n\n// MQTT in node returned the Rhasspy slots in the array msg.payload.slots. \n// This code parses the array into a list of name and value pairs,\n// which will be much easier to work with\n//\nvar slot;\nvar newslots = {}; // create a new empty object\nfor (var i in msg.payload.slots) {\n slot = msg.payload.slots[i]; // one array element at a time\n //new name and value pair\n newslots[slot.slotName] = slot.value.value;\n}\nmsg.slots = newslots; // save back into msg.\nmsg.intent = {};\nmsg.intent.name = msg.payload.intent.intentName;\nmsg.intent.confidence = msg.payload.asrConfidence;\nmsg.sessionId = msg.payload.sessionId;\nmsg.siteId = msg.payload.siteId;\n\n//node.warn(\"ready to return\");\nreturn msg;","outputs":1,"noerr":0,"initialize":"// Code added here will be run once\n// whenever the node is started.\n\nglobal.inSession = false;\n\nvar lastSession = {time: Date.now(), sessionId: \"\", intentName: \"\", confidence: 0 };\nglobal.set(\"lastSession\", lastSession);\n\nreturn;\n","finalize":"","libs":[],"x":410,"y":180,"wires":[["a6cf562714dc6595","725a6649afe169ea"]]},{"id":"a6cf562714dc6595","type":"debug","z":"b74f060c.6b2908","name":"slots extracted","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":700,"y":140,"wires":[]},{"id":"5d0b70fd3a9f7a96","type":"link call","z":"b74f060c.6b2908","name":"","links":["defb47cac878fba1"],"timeout":"30","x":430,"y":1000,"wires":[["ada61f79f15bb868"]]},{"id":"497278a27faff99c","type":"link call","z":"b74f060c.6b2908","name":"","links":["626a7cf1e161a589"],"timeout":"30","x":450,"y":1040,"wires":[["ada61f79f15bb868"]]},{"id":"f058b4b213fc15b6","type":"change","z":"b74f060c.6b2908","name":"Invalid command message","rules":[{"t":"set","p":"payload","pt":"msg","to":"Invalid command","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":440,"y":1120,"wires":[["ada61f79f15bb868","126e7c57a0e452d5"]]},{"id":"802d9c301fe10e25","type":"link call","z":"b74f060c.6b2908","name":"","links":["a39703c7a0c52b1e"],"timeout":"30","x":390,"y":680,"wires":[["ada61f79f15bb868"]]},{"id":"205297a969810602","type":"link call","z":"b74f060c.6b2908","d":true,"name":"intent_timerSet","links":[],"linkType":"static","timeout":"30","x":400,"y":800,"wires":[["ada61f79f15bb868"]]},{"id":"45aadc897818c429","type":"link call","z":"b74f060c.6b2908","name":"intent_timercancel","links":["60d742cd691a5345"],"linkType":"static","timeout":"30","x":410,"y":840,"wires":[["ada61f79f15bb868"]]},{"id":"0fdb581e2d9c6372","type":"link call","z":"b74f060c.6b2908","name":"","links":["c1f781746184e241"],"linkType":"static","timeout":"30","x":380,"y":1080,"wires":[["912fc502dfbbd0c4"]]},{"id":"912fc502dfbbd0c4","type":"template","z":"b74f060c.6b2908","name":"Light Text","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"Christmas is {{slots.state}}!","output":"str","x":600,"y":1080,"wires":[["ada61f79f15bb868"]]},{"id":"c25a5fe4915e4c80","type":"link call","z":"b74f060c.6b2908","name":"","links":["b67399100cf149d2"],"linkType":"static","timeout":"30","x":440,"y":880,"wires":[["ada61f79f15bb868"]]},{"id":"e6889705dcae7372","type":"link call","z":"b74f060c.6b2908","name":"","links":["162c5b523c2f9e2f"],"linkType":"static","timeout":"30","x":430,"y":920,"wires":[["ada61f79f15bb868"]]},{"id":"c4b70ab633dca027","type":"junction","z":"b74f060c.6b2908","x":40,"y":340,"wires":[["13b29f95.47317"]]},{"id":"3e24a26f3fe6c572","type":"mqtt-broker","name":"HA MQTT broker add-on","broker":"192.168.1.98","port":"1883","clientid":"","autoConnect":true,"usetls":false,"protocolVersion":"4","keepalive":"60","cleansession":true,"birthTopic":"","birthQos":"0","birthPayload":"","birthMsg":{},"closeTopic":"","closeQos":"0","closePayload":"","closeMsg":{},"willTopic":"","willQos":"0","willPayload":"","willMsg":{},"userProps":"","sessionExpiry":""}]
Because NR is intercepting the MQTT Intent messages, you should set “Intent Handling” on all your Rhasspy devices to “disabled”.