ChangeLightState intent how to do it properly with node-red

Dear all,
As I am both new to Rhasspy and node-red, I am struggling to make a proper node-red flow to switch on and off a sonoff (with tasmota on it) which controls a light in the salon. I have the following sentence
zet de (salonverlichting|woonkamerverlichting){name} (aan | uit){state}

“aan” means “on” in English
“uit” means “off” in English

I made the thing work but in a very unsatisfying way (I used the intent only for telling me the light was going to be switched on or off) but I needed an extra function to actually make the sonoff switch. To make things even worse the function gets it input before the intent-switch.
Here is my node-red flow:
Could somebody here show me how to do it properly?

kind regards,

Are your different lights controlled in a similar way or in different ways?

Also you will most likely want to use more then one sentence to turn on the lights, i only got german examples for my intents. You will want to use substituitions to always get “aan” or “uit” (or “on” / “off” depending on what you prefere) in your state slot.
This can also be used if you want to refer to the same light with different names.

I made a few changes to your posted flow and put a few comments into it. Not sure if i understand what exactly you want to change or what you want to know.


[{“id”:“e53d302c.94684”,“type”:“tab”,“label”:“Flow 1”,“disabled”:false,“info”:""},{“id”:“ba211585.c123b8”,“type”:“websocket in”,“z”:“e53d302c.94684”,“name”:“rhasspywebsocket”,“server”:“29c2aa0.f16aed6”,“client”:"",“x”:210,“y”:300,“wires”:[[“7e16aeb.da8d55”]]},{“id”:“fa5e071d.83e8c8”,“type”:“change”,“z”:“e53d302c.94684”,“name”:“Unknown intent”,“rules”:[{“t”:“set”,“p”:“payload”,“pt”:“msg”,“to”:“Wat zei u?”,“tot”:“str”}],“action”:"",“property”:"",“from”:"",“to”:"",“reg”:false,“x”:600,“y”:340,“wires”:[[“ad4a0114.d490d8”]]},{“id”:“ad4a0114.d490d8”,“type”:“http request”,“z”:“e53d302c.94684”,“name”:“text to speech”,“method”:“POST”,“ret”:“txt”,“paytoqs”:true,“url”:“http://rhasspy:12101/api/text-to-speech",“tls”:"",“persist”:false,“proxy”:"",“authType”:"",“x”:860,“y”:280,“wires”:[[]]},{“id”:“8ed82a9d.59d758”,“type”:“template”,“z”:“e53d302c.94684”,“name”:“ChangeLightState”,“field”:“payload”,“fieldType”:“msg”,“format”:“handlebars”,“syntax”:“mustache”,“template”:"Ik zet de {{ }} {{ payload.slots.state}}”,“output”:“str”,“x”:610,“y”:280,“wires”:[[“ad4a0114.d490d8”]]},{“id”:“fbda985a.97625”,“type”:“mosca in”,“z”:“e53d302c.94684”,“mqtt_port”:“1883”,“mqtt_ws_port”:8080,“name”:"",“username”:"",“password”:"",“dburl”:"",“x”:110,“y”:60,“wires”:[[]]},{“id”:“fefab438.f4668”,“type”:“inject”,“z”:“e53d302c.94684”,“name”:"",“topic”:"",“payload”:"",“payloadType”:“date”,“repeat”:"",“crontab”:"",“once”:false,“x”:220,“y”:3240,“wires”:[[“b240bf34.fcfbb8”]]},{“id”:“b240bf34.fcfbb8”,“type”:“http request”,“z”:“e53d302c.94684”,“name”:“Relay off”,“method”:“GET”,“ret”:“txt”,“paytoqs”:“ignore”,“url”:“ OFF”,“tls”:"",“persist”:false,“proxy”:"",“authType”:"",“x”:420,“y”:3240,“wires”:[[]]},{“id”:“97b12bb6.444e18”,“type”:“inject”,“z”:“e53d302c.94684”,“name”:"",“topic”:"",“payload”:"",“payloadType”:“date”,“repeat”:"",“crontab”:"",“once”:false,“x”:220,“y”:3280,“wires”:[[“611d9130.4ecfe8”]]},{“id”:“611d9130.4ecfe8”,“type”:“http request”,“z”:“e53d302c.94684”,“name”:“Relay on”,“method”:“GET”,“ret”:“txt”,“paytoqs”:“ignore”,“url”:“ ON”,“tls”:"",“persist”:false,“proxy”:"",“authType”:"",“x”:420,“y”:3280,“wires”:[[]]},{“id”:“f2372bc3.745f28”,“type”:“http request”,“z”:“e53d302c.94684”,“name”:“Relay toggle”,“method”:“GET”,“ret”:“txt”,“paytoqs”:“ignore”,“url”:“ TOGGLE”,“tls”:"",“persist”:false,“proxy”:"",“authType”:"",“x”:430,“y”:3320,“wires”:[[]]},{“id”:“176a10a7.df4a6f”,“type”:“inject”,“z”:“e53d302c.94684”,“name”:"",“topic”:"",“payload”:"",“payloadType”:“date”,“repeat”:"",“crontab”:"",“once”:false,“x”:220,“y”:3320,“wires”:[[“f2372bc3.745f28”]]},{“id”:“198eaee.b76f6d1”,“type”:“http request”,“z”:“e53d302c.94684”,“name”:“Status”,“method”:“GET”,“ret”:“txt”,“paytoqs”:“ignore”,“url”:“",“tls”:"",“persist”:false,“proxy”:"",“authType”:"",“x”:410,“y”:3380,“wires”:[[“2d0590a.d1d497”]]},{“id”:“7cb2a819.0e32e”,“type”:“inject”,“z”:“e53d302c.94684”,“name”:"",“topic”:"",“payload”:"",“payloadType”:“date”,“repeat”:"",“crontab”:"",“once”:false,“x”:220,“y”:3380,“wires”:[[“198eaee.b76f6d1”]]},{“id”:“2d0590a.d1d497”,“type”:“debug”,“z”:“e53d302c.94684”,“name”:"",“active”:false,“console”:“false”,“complete”:“false”,“x”:610,“y”:3380,“wires”:[]},{“id”:“702cf655.f6ea88”,“type”:“comment”,“z”:“e53d302c.94684”,“name”:"HTTP control”,“info”:“Controlling the relay via HTTP\nHTTP API in the admin page need to be turned off\nCopy the api key from the admin page\n\nURL http:///api/relay/0?apikey=&value=0\naccepted values (at the end):\n0: off\n1: on\n2: toggle”,“x”:170,“y”:3200,“wires”:[]},{“id”:“7e16aeb.da8d55”,“type”:“switch”,“z”:“e53d302c.94684”,“name”:“Intent”,“property”:“intent”,“propertyType”:“msg”,“rules”:[{“t”:“eq”,“v”:“ChangeLightState”,“vt”:“str”},{“t”:“else”}],“checkall”:“true”,“repair”:false,“outputs”:2,“x”:400,“y”:300,“wires”:[[“8ed82a9d.59d758”,“15cd1247.455c36”],[“fa5e071d.83e8c8”]]},{“id”:“6b67d05a.e4a0d”,“type”:“mqtt out”,“z”:“e53d302c.94684”,“name”:"",“topic”:“cmnd/sonoffsalon/power”,“qos”:“0”,“retain”:"",“broker”:“9ff5363.ec7e2c8”,“x”:1010,“y”:100,“wires”:[]},{“id”:“f964af90.6af83”,“type”:“inject”,“z”:“e53d302c.94684”,“name”:"",“topic”:"",“payload”:“off”,“payloadType”:“str”,“repeat”:"",“crontab”:"",“once”:false,“x”:450,“y”:60,“wires”:[[“6b67d05a.e4a0d”]]},{“id”:“bd08b517.bb7fc8”,“type”:“inject”,“z”:“e53d302c.94684”,“name”:"",“topic”:"",“payload”:“on”,“payloadType”:“str”,“repeat”:"",“crontab”:"",“once”:false,“onceDelay”:"",“x”:450,“y”:100,“wires”:[[“6b67d05a.e4a0d”]]},{“id”:“fbfdd735.c93ed”,“type”:“comment”,“z”:“e53d302c.94684”,“name”:“MQTT control”,“info”:“Controlling the relay via MQTT\n\nroot topic followed by relay/0/set\naccepted messages:\n0: off\n1: on\n2: toggle”,“x”:470,“y”:20,“wires”:[]},{“id”:“15cd1247.455c36”,“type”:“switch”,“z”:“e53d302c.94684”,“name”:“device”,“property”:“”,“propertyType”:“msg”,“rules”:[{“t”:“eq”,“v”:“salonverlichting”,“vt”:“str”},{“t”:“eq”,“v”:"",“vt”:“str”}],“checkall”:“true”,“repair”:false,“outputs”:2,“x”:570,“y”:180,“wires”:[[“c0ca2162.4595d8”],[]]},{“id”:“c0ca2162.4595d8”,“type”:“change”,“z”:“e53d302c.94684”,“name”:“replace with on/off”,“rules”:[{“t”:“set”,“p”:“payload”,“pt”:“msg”,“to”:“slots.state=“aan” ? “on” : slots.state=“uin” ? “off””,“tot”:“jsonata”}],“action”:"",“property”:"",“from”:"",“to”:"",“reg”:false,“x”:750,“y”:160,“wires”:[[“6b67d05a.e4a0d”]]},{“id”:“c8179aa.e8427e8”,“type”:“comment”,“z”:“e53d302c.94684”,“name”:"",“info”:“Websocket changed to complete message instead of just payload\n-> msg.intent; msg.slots … can now be used”,“x”:170,“y”:220,“wires”:[]},{“id”:“b06e6700.e68e2”,“type”:“comment”,“z”:“e53d302c.94684”,“name”:"",“info”:“Swith node to filter for different intents”,“x”:380,“y”:220,“wires”:[]},{“id”:“11b517f6.6b6ef”,“type”:“comment”,“z”:“e53d302c.94684”,“name”:"",“info”:“Device filter in case your different lights are handled differently\n\ninstead of replacing it here you could also use substitutions for your slots values in rhasspy”,“x”:610,“y”:120,“wires”:[]},{“id”:“b95d1b1b.9525a”,“type”:“comment”,“z”:“e53d302c.94684”,“name”:"?",“info”:"",“x”:130,“y”:100,“wires”:[]},{“id”:“29c2aa0.f16aed6”,“type”:“websocket-listener”,“z”:"",“path”:“ws://rhasspy:12101/api/events/intent”,“wholemsg”:“true”},{“id”:“9ff5363.ec7e2c8”,“type”:“mqtt-broker”,“z”:"",“name”:“localhost:1883”,“broker”:“localhost”,“port”:“1883”,“clientid”:"",“usetls”:false,“compatmode”:true,“keepalive”:“60”,“cleansession”:true,“birthTopic”:"",“birthQos”:“0”,“birthPayload”:"",“closeTopic”:"",“closeQos”:“0”,“closePayload”:"",“willTopic”:"",“willQos”:“0”,“willPayload”:""}]

Thanks for trying to help. To keep it simple I’d use a sonoff esp001 in the salon and another one (esp002) in the woonkamer. Currently the flow is only made to control esp001, but I think I can handle that once the current flow is corrected.
The thing which is most disturbing in my current flow is I don’t have a complete function-node or template-node after the intent-switch which both gives the vocal confirmation of the action done and which also performs the switching action. Or otherwise stated: the on_or_off function-node should be merged with the ChangeLightState template-node such that the ChangeLightState-node is left in its current position.
I tried to import your flow but I can’t because of an error:

Import to
SyntaxError: JSON.parse: expected property name or ‘}’ at line 1 column 3 of the JSON data

kind regards,

Can you get feedback from the device if the state has actually changed ?

Yes, I can get feedback from the device I have added two nodes to the flow so you can see:

kind regards,

It would make sense to use that for the response so it will only say it turned on when it actually turned on the light. Not sure what the best way is to only give a TTS response if the state changes for like 4s or so after the intent was recognized. But that would make the most sense to me. I do not give TTS output for my light intents which is why i have not tried to do something like that yet.

Could you show me how your “switchonoff-node” is made? Is it a function-node? Or a template-node?
What’s its contents?

kind regards,

It is a bunch of nodes as i got enocean relays that are controlled with fhem and an adressable led strip also connected to the second output of the relay. The addressable led strip is controlled with an esp8266 which runs a custom programm and accepts commands over mqtt. To make it even more difficult i also have a small touch display to control the lights which also reflects the status of the lights…
Its complicated but now that i soldered the connections it works reliable. I will leave it alone until i need to add more lights to it.

Based on an article by Koan in the Dutch magazine Computertotaal juli/augustus 2020 (p.58…63) and with the kind help of a lot of kind free software minded people, I made a flow which works.
The hardware consists of:

  • A raspberry pi with a respeaker 2 hat
  • A sonoff with tasmota to control the “salonverlichting” (=light in the rest room)
  • A nodemcu-module with espeasy and a DHT-11 sensor which measures temperature and humidity

The following intents are implemented:

  • ChangeLightState, currently this controls only one sonoff (the one in the salon) but the idea is to add more sonoffs for controlling lights in other rooms
  • GetTime, tells the time (the code for it is from Koan), there is a little linguistic detail which could be improved: when the time is e.g. 15:03, the system says “het is 15 uur nul drie”, it would be better if it said: “het is 15 uur drie”
  • GetTemperature, gets the temperature value of the DHT-11 connected to the nodemcu esp001,
    Currently there is only one nodemcu in one room but the idea is to have more nodemcu’s in the future and to ask for the temperature in the different rooms.
  • GetNetworkState, checks whether the raspberry is able to ping the belnet-server (a very reliable server)
    Currently only the belnet-server is pinged but the idea is to extend this to a system for pinging other sites too e.g. my modem-router
  • WhatsTheWeather, this gets the weather-information from OpenWeather (don’t forget to enter your API-key), it is possible to differentiate between different cities you are interested in
  • GetGarageState is not implemented

There is one bug in the system which puzzles me:
When I speak out a nonsense phrase after the wakeword, the system reacts with the two beeps but
doesn’t say anything. When I say the wakeword again it responds two times “Ik begreep jou niet, wat zei je”. If however I enter the same nonsense phrase textually in the Rhasspy home web page, it reacts immediately correctly saying “Ik begreep jou niet, wat zei je”

Here are my sentences:

zet de (salonverlichting | woonkamerverlichting | woonkamerlamp | garageverlichting){name} (aan | uit){state}

hoe laat is het
wat is de tijd

wat is de temperatuur (in|op){voorzetsel} (de|het|){lidwoord} (salon|zolder|kelder|bureau){kamer}
hoe (warm | koud) is het (in|op){voorzetsel} (de|het){lidwoord} (salon|zolder|kelder|bureau){kamer}

is de garagepoort (open | gesloten)

test de connectie met (belnet|modem){site}
test de verbinding met (belnet|modem){site}
test connectie met (belnet|modem){site}
test verbinding met (belnet|modem){site}

welk weer is het in (Zottegem | Brussel | Gent){city}
hoe is het weer in (Zottegem | Brussel | Gent){city}
het weer is in (Zottegem | Brussel | Gent){city}
wat is het weer in (Zottegem | Brussel | Gent){city}
hoe warm is het in (Zottegem | Brussel | Gent){city}

and here is my nodered-code (I tried to put it in this message, but I couldn’t because of the message Body limit which is 32000 characters):

kind regards,
p.s. I cannot guarantee that the link will remain active in the future

1 Like

Just a small tip for later:
If you use rules like this you will have an easier time extending your sentences later.
will get replaced with the part after city=
(Only works in the same intent. Otherwise <> would be required.)

city = (Zottegem | Brussel | Gent){city}
welk weer is het in <city>
hoe is het weer in <city>
het weer is in <city>
wat is het weer in <city>
hoe warm is het in <city>

Thanks for the suggestion, I have adapted my sentences accordingly (I tried to do a block quote,
but the “less than”-signs in the code interfere with “the less than”-signs for the layout. I should probable
escape them somehow?

> [ChangeLightState]
> name=(salonverlichting | woonkamerverlichting | woonkamerlamp | garageverlichting){name}
> state=(aan|uit){state}
> zet de   <name><state>
> [GetTime]
> hoe laat is het
> wat is de tijd
> [GetTemperature]
> kamer = (salon|zolder|kelder|bureau){kamer}
> lidwoord = (de|het){lidwoord}
> voorzetsel = (in|op){voorzetsel}
> wat is de temperatuur <voorzetsel><lidwoord><kamer> 
> hoe (warm | koud) is het  <voorzetsel><lidwoord><kamer>
> [GetGarageState]
> is de garagepoort (open | gesloten)
> [GetNetworkState]
> site = (belnet|modem){site}
> test de connectie met <site>
> test de verbinding met <site>
> test connectie met <site>
> test verbinding met <site>
> [WhatsTheWeather]
> city = (Zottegem | Brussel | Gent){city}
> welk weer is het in <city>
> hoe is het weer in <city>
> het weer is in <city>
> wat is het weer in <city>
> hoe warm is het in <city>

kind regards,

B.t.w. do you have any idea what causes the bug I mentioned? It seems like some kind of caching/buffering issue to me.

kind regards,