silveradept: A kodama with a trombone. The trombone is playing music, even though it is held in a rest position (Default)
[personal profile] silveradept
When we last left this set of fire lizards in a trenchcoat, there were a few devices and a voice assistant that could be used to direct intents and make the Castle do things.

Since then, a few new devices have joined the cavalcade since we last left, same of which only do so after having to extract some keys from a web service that required the installation of a terrible app on one of the somewhat discarded devices not currently in production use. Just so that we could use an integration that doesn't require using a cloud service call for those things. (Or, not too much of one.)

Things are still pretty fantastic at this point, but we're still somewhat limited in there things it could do having to be mostly predefined, either by evaluating an already existing sensor value, sending a specific command, or by hard coding a value somewhere in the construction of the intent or in the object to be acted upon. The only thing that had some amount of dynamism in it was that entities recognized as lights could have their brightness set in values from 0-100 and colors as pulled from an official list of possible colors.

Most frustrating at that point was that there didn't appear to be any way of doing what seemed to be a basic function other assistants could do: set a timer of an arbitrary length and start it. Well, I didn't understand how to do it correctly, I should say. I could look at all of the documentation for the assistant and for the intent script and understand that what I wanted to do was catch the intent JSON, parse it, and then do stuff based on that, but there aren't any tutorials on how to do that or any high-level understanding of how that gets done on the exchange so that I could learn how to do the thing. For example, the built-in intent handling example for Home Assistant from Rhasspy provides slot programs and sentence files to make things work, but it doesn't explain what's going on underneath the hood. The intent handling documentation for Rhasspy doesn't help, and the intent script documentation in Home Assistant is extremely barebones.

There's an assumption underlying all of this that people who want to do this kind of work are willing to learn how to completely understand Python (the language Home Assistant is written in) if they want to learn how to do things that aren't provided by a component, be it a sensor, a state, or some other thing where evaluating or reading a value is useful. I don't actually know how to program Python from the ground up, so a lot of what might otherwise be accessible to me is waiting for me to gain sufficient knowledge of programming Python (and understanding it and its objects at a more fundamental level) before I can do anything with it. In the absence of this underlying assumed knowledge, I am instead behaving like an information professional and seeing if I can crib from someone else's sources about all of this.

My search engine skillz were apparently lacking before, but I did manage to luck into a post where someone posted both their setup in Rhasspy and their setup in Home Assistant. And with links to what they had used before, so finally, I had some code to study. (It's mostly in German). At which point I learned that apparently you can embed logic in the scripting if you know how to escape it properly. Which was one of the key things I needed to understand about setting arbitrary timers. Now that I know I can add logic and conditionals to the scripts, that helps make things easier to puzzle out. Even better, the intent script code is meant to handle parameters passed from Rhasspy. So now, instead of a box that makes a lot of assumptions about what you already know and gives you a minimalist example as a proof of concept, I've got the whole thing to work with and study until I understand. The minimalist example that I've been trying to osmose has completely failed to point out that there is a syntax for passing parameters from Rhasspy to Home Assistant that can be caught and manipulated on the Home Assistant end (although there seems to be an increasing preference for using something called Node RED to act as an intermediary between Rhasspy (or other things) and Home Assistant).

In any case, copying and pasting works well enough that I can see the flow and understand what's being passed from voice assistant to Home Assistant, and for the first few attempts at setting an arbitrary timer length, it seems to work. Hooray!

However.

As soon as I start trying to put in more complex timers, I stop getting the kinds of results that I want. Trying to set timers with words in the twenties through the fifties rarely gets me the time that I want (and also, because Rhasspy doesn't like either dashes or underscores, apparently, in names of things, I have to do some amount of smashing the words together to get it to work correctly.) So I stare at Rhasspy to see what it believes I said, and therefore what parameters get passed, versus what I actually said. After a bit of observing how the translating is going, I realize the problem I'm having is because the variable definitions for those numbers are set up rules that a word like fifty-nine, which would need a dash to render properly, are instead being interpreted as two words with a space between them (fifty nine, or 50 9) and therefore, based on the rules that I had copied and pasted in, there was no way Home Assistant could have understood me correctly, because Rhasspy was functioning correctly. So, it was time to simplify. In the barebones example of the lights, there was a syntax to say "Hey, accept any (presumably integer) value from 0-100 for this parameter." So I torched the complicated translation rules and rewrote the statements to use the built-in capacity for recognizing and properly translating numbers across to Home Assistant.

That worked fine to get the correct integers across to Home Assistant, but simplifying on one end had created a small complication on the other, because Home Assistant expects the duration value for length of a timer to be sent as a string, formatted "HH:MM:SS", and there isn't any implicit type conversion going on between the integers being sent by Rhasspy and the string format that Home Assistant is expecting. (Because that's a weirder thing to do. Strings to ints is something that happens very often, so implicit type conversion would make sense there, especially if there's a math operation being asked for. Ints to strings is not usually the way things go, so there's not a built-in conversion.) Thankfully, since we had just added a new sensor to the household, I had reason to have the (extremely brittle) script I feed into xscreensaver to retrieve temperature and humidity from the sensors open. (The library it relies on is also extremely poorly documented, but does at least provide code snippets for examples.) Temperature and humidity are sent as floating-point numbers (floats) from the sensors. Temperature is in degrees Celsius, so the script itself has to perform some math on the floats to convert to Fahrenheit, another math operation to round the result to 0 significant digits (i.e. the greatest whole number), and then to format the final result as a string so it can be concatenated in with other strings for display. Humidity still requires the rounding of the float and the formatting of the result into a string. The important part is that I know the syntax works, and conveniently, the script is also written in Python. So I borrow the formatting syntax from the sensor script and restart to make sure everything is up to date in the newest material, and then I start setting timers and watching the output as passed to Home Assistant and what durations of the timer it saets in response. Everything runs perfectly smoothly, even with words that had been giving me grief.

[Victory Fanfare of your choice goes here!]

Finally, I have been able to set an arbitrary-length timer through voice commands! This is extremely exciting.

(The housemates have ideas for improvement, specifically to try and get around the problem that if a timer is running, calling to set a new timer will replace the old one. They've proposed that the script contain logic to assign the new timer to whichever of the timer pool available is idle instead of running, and that if all the timers are currently running, to read out the available time left on all of the timers so they know how long they'll have to wait. Which, now that I know how to put logic into the scripts, seems less impossible than it was before. It'll take some time, because now I have to wrestle with datetime objects, timedeltas, and yet again, formatting conversions with objects to get them into string form so that they can then be passed to the speech system to be read aloud. Comparatively, the "assign this to the first available timer" logic should be doable to implement, although I'll bet there's an elegant way of doing it that makes all the comparisons in a single line rather than a series of ifs and elselifs.)
If you don't have an account you can create one now.
HTML doesn't work in the subject.
More info about formatting

If you are unable to use this captcha for any reason, please contact us by email at support@dreamwidth.org

Profile

silveradept: A kodama with a trombone. The trombone is playing music, even though it is held in a rest position (Default)
Silver Adept

April 2025

S M T W T F S
   12345
6789101112
131415 16171819
20212223242526
27282930   

Most Popular Tags

Style Credit

Expand Cut Tags

No cut tags
Page generated Apr. 23rd, 2025 12:41 pm
Powered by Dreamwidth Studios