Rhasspy can tell you the weather (at least if you speak German)

Evening,

the last few days or so I spent porting my fork of the “Snips-Wetter” skill to rhasspy and as of now I got it working and put it onto github.

It can be found here if anyone wants to try it out.

If anyone decides to try it out I would love feedback (or even some help to fix bugs and add in new features). I am afraid the code is one big mess at the moment but it is at least partly documented and works.

Anyone wanting to translate it is welcome, if you can brave the code.

Daenara

4 Likes

This looks very nice, Glückwunsch! - I have just done something simple with openweathermaps in node-red, but maybe I will translate yours to English and call the script. It would be very nice to have such a weather-module that generates decent text for the right questions.

Looks great. I’ll give it a try tomorrow

I could not fall asleep so I did a bit of cleanup work and restructuring. There is a small config file now (thought not everything is implemented yet and leaving the wrong value blank will throw errors) and I split the big logic file up. Way more imports to be done but it is at least better readable. I will split everything concerning language up into an extra file in the next few days which should make translating easier.

If I understand it, it only can be used with Homeassistant.
The result is not automatcally send back to output of asking rhasspy.
I use nodered in Docker.

Really? I am new to rhasspy but I run rhasspy from a docker on the same machine I run a hass docker and I have no problems.

The script itself doesn’t interact with homeassistant at all and I set it up exactly like the documentation said for rhasspy. It should run fine.

Only scenario I can think of is if the speech output from my custom command only works in conjunction with homeassistant but I saw no mention of that anywhere.

That’s what I guess.

 If handle.forward_to_hass is false, the output of your program is not used.

So if I do not have hass, your output will be ignored.

Possible solutions:

  • sending output with mqtt (v2.5??)
  • sending output with api (Problem: no IP, just siteID is sended)

WTF… For what reason does rhasspy have a custom command option if the output is ignored without homeassistant… That seems pretty counterintuitive since it lists custom commands as a possible alternative.

Well, seems like I need to put an alternate command_config.py out there that uses the api for tts. It does not actually effect my script at all, I just describe a general setup in the readme to help ppl out with how to actually call the script. Nothing there stopping ppl to send the output string per api or mqtt anywhere they want from their custom command script.

Thanks for pointing that out. I tried using it with forward_to_hass set to false before but since I still test using the web gui I did not notice the checkbox overriding the settings.

On another note I found something I broke when I worked on it yesterday so back to programming for me.

Ah, it turns out I was hoping too much. I was hoping to have some kind of command line tool that could give you a weather forecast/answer for specific parameters given on the command line. Then I could have sent this result via network to tts to my correct satellite. I basically do all my actions in node-red. I did like your ideas of different questions to ask -but I think I can unfortunately not integrate this into my setup.

Anybody knows of any good weather forecast command line tools?

You can re purpose the logic of this thing for that. If you write a parser that takes command line parameters and puts them into a WeatherRequest object and use that instead of rhasspy_wheather.py then you will get a text response from command line parameters.

If you want the values directly instead of the text response then you can just grab the WeatherForecast object and use that.

The only thing you would need to do is fork it and then replace rhasspy_weather.py with something that works with node-red. The logic for getting the weather and answering is separate from that.

I’m using that:
weather

It uses msg.slots.city as input and gives you some Information as output. Output it is said by calling satellite:

[{“id”:“9bc5e254.49b41”,“type”:“openweathermap”,“z”:“21f11887.571048”,“name”:“GetWetter”,“wtype”:“current”,“lon”:"",“lat”:"",“city”:"",“country”:"",“language”:“de”,“x”:1400,“y”:920,“wires”:[[“8bd9a746.6facc8”]]},{“id”:“8bd9a746.6facc8”,“type”:“function”,“z”:“21f11887.571048”,“name”:“SpeakWeather”,“func”:"id = msg.data.weather[0].id;\n\nmsg.payload = "Das Wetter in " + msg.payload.location + ": " + msg.payload.detail + ". " + \n “Die derzeitige Temperatur beträgt " + msg.payload.tempc.toFixed(1).replace(’.’, ‘,’) + " Grad. " +\n “Es gibt eine Luftfeuchte von " + msg.payload.humidity + " Prozent “\n\n\nreturn msg;”,“outputs”:1,“noerr”:0,“x”:1560,“y”:920,“wires”:[[“93bfaa03.be2c78”]],“icon”:“node-red-node-openweathermap/weather.png”},{“id”:“87f49b03.778728”,“type”:“function”,“z”:“21f11887.571048”,“name”:“setCity”,“func”:”\nmsg.location = {“city” : msg.slots.city,\n “country” : “Germany”}\n\nreturn msg;”,“outputs”:1,“noerr”:0,“x”:1250,“y”:920,“wires”:[[“9bc5e254.49b41”]],“icon”:“node-red-node-openweathermap/weather.png”}]

the node is:

node-red-node-openweathermap

to answer, I save the callers siteId in a global variable and send the output to this switch
answer

[{“id”:“997b2108.5551d”,“type”:“http request”,“z”:“27a1dcd5.ab7ad4”,“name”:“Wohnzimmer_”,“method”:“POST”,“ret”:“txt”,“paytoqs”:false,“url”:“http://10.2.30.1:12101/api/text-to-speech",“tls”:"",“persist”:false,“proxy”:"",“authType”:“basic”,“x”:640,“y”:300,“wires”:[[]]},{“id”:“76f4e8b9.1d15b8”,“type”:“switch”,“z”:“27a1dcd5.ab7ad4”,“name”:“global.siteId”,“property”:“siteId”,“propertyType”:“global”,“rules”:[{“t”:“eq”,“v”:“wohnzimmer”,“vt”:“str”},{“t”:“eq”,“v”:“küche”,“vt”:“str”},{“t”:“eq”,“v”:“schlafzimmer”,“vt”:“str”},{“t”:“eq”,“v”:“badezimmer”,“vt”:“str”}],“checkall”:“false”,“repair”:false,“outputs”:4,“x”:430,“y”:360,“wires”:[[“997b2108.5551d”],[“dbf7b998.d91028”],[“bfbd763.9fd9288”],[“6159d91e.4b7518”]]},{“id”:“6159d91e.4b7518”,“type”:"http request”,“z”:“27a1dcd5.ab7ad4”,“name”:“Badezimmer_”,“method”:“POST”,“ret”:“txt”,“paytoqs”:false,“url”:“http://10.2.30.4:12101/api/text-to-speech",“tls”:"",“persist”:false,“proxy”:"",“authType”:“basic”,“x”:640,“y”:420,“wires”:[[]]},{“id”:“dbf7b998.d91028”,“type”:"http request”,“z”:“27a1dcd5.ab7ad4”,“name”:“Küche______”,“method”:“POST”,“ret”:“txt”,“paytoqs”:false,“url”:“http://10.2.30.2:12101/api/text-to-speech",“tls”:"",“persist”:false,“proxy”:"",“authType”:“basic”,“x”:640,“y”:340,“wires”:[[]]},{“id”:“bfbd763.9fd9288”,“type”:"http request”,“z”:“27a1dcd5.ab7ad4”,“name”:“Schlafzimmer”,“method”:“POST”,“ret”:“txt”,“paytoqs”:false,“url”:“http://10.2.30.3:12101/api/text-to-speech",“tls”:"",“persist”:false,“proxy”:"",“authType”:“basic”,“x”:640,“y”:380,"wires”:[[]]}]

Right now it only tells the basic information, but you can change that in the “SpeakWeather” node
I hope you enjoy.

Ok, forked it and trying to write a parser now that will output the text on stdout. If I get anywhere, you will receive a pull request.

Thanks, I have been doing it exactly this way already.
But it just gives you the current weather - I am looking forward to answering more questions and generating some kind of more complete forecast report. And having that as a cli tool would allow it to be embedded in multiple environments - not only hass, node-red, and rhasspy.

Not exactly what you are asking but I use the DarkSky node-red node to collect weather information and then when I ask for the weather, node-red handles the intent and publishes the information to mqtt on hermes/tts/say. For tts I use MaryTTS.

Does darksky generate some kind of human readable forecast for you that you can directly send to tts?

Openweather has this as “forecast”: “broken clouds” - that’s not enough for me, that’s why I like @Daenara module so much.

Compliments, things are developing fast! I am wondering if I wait until next week to really add something, not sure I can keep up at the moment :wink:

I like the idea of importing the language specific things from locale folder, it could also eventually all go into a config file (could be even python)? and then have language specific templates for that which could be customized for a particular setup?

I see a parser folder, but this is not yet a command line parser eating the detected intent directly, is it? What do you have in mind for getting something fed via command line? It could also be json via stdin if that’s easier, but I think command line parameters would be more unix’ish.

It has a lot of information but I then use node-red to build the response like this

    value = "currently, " + global.get("HornsbyWeatherCurrent.Conditions") + 
            " and " + Math.round(global.get("HornsbyWeatherCurrent.Temperature")) + 
            ". Expected " + global.get("HornsbyWeatherForecast0.Conditions") +
            " with a high of " + Math.round(global.get("HornsbyWeatherForecast0.HighTemperature")) + 
            " and a low of " + Math.round(global.get("HornsbyWeatherForecast0.LowTemperature"));

Where I have stored the fields in an object like:
{“thingname”:“HornsbyWeatherCurrent”,“Comfort”:“Warm”,“Conditions”:“Partly Cloudy”,“Humidity”:85,“LocationUsed”:"-33.6,151.1 - Australia/Sydney",“LocationUsedText”:"-33.6,151.1 - Australia/Sydney",“Pressure”:1026.1,“RainFallDay”:0.2217,“RainFallsource”:“weather”,“THI”:15.08,“Temperature”:15.08,“WindDegrees”:157,“WindDirection”:“SE”,“WindGust”:1.7,“WindSpeed”:1.06,“Windsource”:“weather”,“source”:“weather”}
and
{“thingname”:“HornsbyWeatherForecast0”,“Conditions”:“Mostly cloudy throughout the day.”,“HighComfort”:“Warm”,“HighHumidity”:97,“HighPressure”:1026.1,“HighTHI”:22.2,“HighTemperature”:24.79,“HighWind”:2.94,“HighWindDegrees”:272,“Highsource”:“weather”,“LowComfort”:“Cold”,“LowHumidity”:65,“LowPressure”:1019.7,“LowTHI”:2.19,“LowTemperature”:13.93,“LowWind”:0.73,“LowWindDegrees”:29,“Lowsource”:“weather”,“POP”:44,“source”:“weather”}

I collect 7 days forecast at present I just provide current information and the forecast for the coming day.

So the above would read out:
“currently, Partly Cloudy and 15. Expected Mostly cloudy throughout the day. with a high of 25 and a low of 14”

Thanks for the compliment. I have quite a bit of free time right now so I work on the script quite a bit. The major restructuring should be done now, I did it as fast as I could so me moving things to different files will not break to much later on (I broke everything 5 times yesterday and followed the errors to repair it).

The plan for the locale is to eventually have files that contain ever bit of text that is either input or output and ppl can configure quite a bit with that. The locale moved into the config but so far every config needs to be mentioned in the code to work and I see no way around that at the moment and no need to do research on it. If you have any other ideas for the locale, I would be happy to read about them.

As for the parser folder. I separated the parsing logic from the main program to make it more modular. The parser is (so far, same problem as the locale file) selected via import in rhasspy_weather.py. So basically the only thing someone needs to do to parse a different input would be to replace the file in the import and rewrite the function.

I personally have no plans for a command line parser because for me the current one works fine but if you want to write one put the logic into the parser folder and copy, rename and edit rhasspy_weather.py to work with it.

As for waiting a week before you start, you can do that. By then my new pi will hopefully be here and I will have something else to do but I think the major work that breaks stuff is done. Plans for today are moving the language stuff to the locale file, maybe do some testing (and the resulting bug fixes) and some small work. If you only plan to write yourself a parser then you should be pretty safe from stuff there being broken.

Regarding the combination of configuration and language. My idea was to have some sample configuration templates for different languages, but maybe you are right that it’s better to separate them into two config files. One (config.py) has names of topics, intents, default verboseness, log level, … in it, the other (language.py) all the language specific stuff. Of course that could also go into some kind of ini-file in .config/thasspy_weather, but you need to decide what fits your workflow best.

Problem regarding the command line parser for python is that I seem to always do it a different way - so I will read another comparison and then decide - should be easy to build.

Correction, I did that today, not only for the locale but also for the parser and api. Now the script will try to load a file that is named exactly like what is written in the config.ini, if the file does not exist, it just throws an error.

That is exactly what I am doing right now. If you want to teach the script another language (and I am done with the localisation movement) you can just copy the german.py, rename it to your language, translate everything and name the new language in the config instead of german. The locales are python files directly because I foresaw (and got to that point today) that there will be a need for language specific functions anyway so it is easier to just have that as a python file instead of an ini.

The main goal is you can download it and set it up with minimal effort but if you want to customize the answers more than just changing the word order around you will need to edit code, so you can also edit code for the locale. I don’t see anyone without coding knowledge wanting to translate it anyway, so no need to write a complicated ini parser for that stuff.

As a side note, I finally figured out how to quote stuff, have been wondering how to do that for a few days now.