silveradept: A librarian wearing a futuristic-looking visor with text squiggles on them. (Librarian Techno-Visor)
Silver Adept ([personal profile] silveradept) wrote2021-04-23 12:18 am

Adventures In Home Automation #2: The Correct Way To Do Something Is The Way That Works

Oh, boy, did I have a fun time this weekend and vacation period getting things set up and trying to make my home automation more powerful and better able to respond to requests from the household. Two stellar examples emerge of problem-solving, scratching my head at the documentation, and trying things iteratively until something worked the way I intended it to. I feel like this is something I should be taking more pride in, since they were both successful, but mostly what's come out of them has been feelings of embarrassment at it taking this long, or that the solution that I've hit upon is probably Ugly and Wrong, or disbelief that I managed to make it work at all, since they look so long and so much effort to do. So, I'm laying these examples out for you, partly as a bid for your opinions on the matter so that I can recalibrate reality against brainweasels, and partly because I want to document that these things really did end up working.

Example 1: The TV and the Pi

In anticipation of the upcoming hot season, a second IR remote and accompanying sensor cable arrived and needed to be installed in the bedroom. Such a wide-band broadcast device would then be connected to the Home Assistant so that rules regarding temperature differentials could be incorporated such that fans and air conditioners would automatically turn themselves on to keep rooms cool and the air well-circulating. (The original model IR remote and sensor device will also be employed in this manner once its corresponding device is installed for the hot season.) I thought a good placement would be to have a good spread across the room and to control both climate devices and the screen that is in the room. The first possible idea didn't work because power cables didn't extend far enough to keep the new device happy and in place. So I fell back to a second idea that wasn't exactly what I wanted, but was definitely something that I could use.

At which point I discovered one problem and rediscovered another. The discovery? The air conditioner's IR receiver is placed somewhere difficult to achieve a consistent signal from the initial idea.

The rediscovery? The screen in the bedroom's IR receiver is basically busted. Even with the universal remote and codes downloaded specifically for it, I pointed and pushed buttons from only a few inches away from the nominal receiver space and nothing happened. Zip, nope, nothing.

Using the air conditioner's remote, I was able to determine where a consistent signal could be sent from and repositioned the IR remote to a new spot that could still control the fan and the air conditioner. Which left only the problem of the screen and its control, because a busted IR receiver means that we have to use other methods if we want to control the screen in any way other than manually turning it on or off. When you're watching programming in bed, you don't want to have to get up to turn off the screen and then come back to bed, especially if you have a drowsing or sleeping partner…or if moving would disturb the cat.

In my favor, I already know the screen can be controlled from the physically attached Raspberry Pi 4 using HDMI Consumer Electronics Control, as I have already installed all of the appropriate libraries and successfully sent CEC commands from the Pi terminal to turn the screen both on and off. Further in my favor, I've already followed directions to put a script and Start menu-equivalent entry onto the Raspberry Pi to call and execute the appropriate CEC command to turn off the screen, and that script works quite well, so I can use the wireless keyboard/mouse combo that I use to navigate the computer and then keep it bedside for the night, or something like that. So, these things are possible.

The first and most obvious solution to me, then, is that if I can use a secure shell from the Home Assistant to the Raspberry Pi, I can make the Home Assistant run the appropriate script on the Pi, the correct CEC command gets sent to the screen, and the whole thing can then be controlled using the voice assistant, so that the screen can be turned on or off from the bedroom by voice.

I followed a tutorial about how to set up key-based authentication between Home Assistant and another computer, and it worked in the terminal extension that I had installed to Home Assistant! I could send a single SSH command with the request to run the script, and the screen turned on and off. Woo!

Except.

I then tried to replicate my successes with the appropriate shell_command configuration in the core Home Assistant and it bombed. Error 255 every single time, even though the script worked in the terminal add-on. As I found out, looking in the Github issues, something that works in an add-on container doesn't necessarily mean it will work in Home Assistant core. Which, now that I'm writing this up, is something I had to learn before as well, when I wanted the core home assistant to play a media file, and instead ended up having to install an add-on that provided a media player device that could be invoked for playing a local media file. "Core" is very "core", and the expectation seems to be that one uses add-ons for anything not core at all.

Anyway, first option shot. In the back of my head, I knew that Home Assistant had a CEC add-on, but I had assumed, especially since the platform it's running on had an HDMI output, that CEC control on Home Assistant would be restricted to what it could locally find through that HDMI port. Bad assumption, as in the documentation, it turns out Home Assistant can listen and control devices with CEC over the network, assuming the target machine is running the appropriate pyCEC server. Which eventually meant downloading the appropriate module, and then downloading and compiling from source the library that the module depended on (even though there was a libcec already installed, and one that supposedly had python bindings to use, and…ugh, whatever) and then running the appropriate module, which started an appropriate server for Home Assistant to find and use.

However, because this screen is as difficult as it can be, calling the "power_on" service described in the documentation doesn't do squat for turning on the screen. The "standby" service works properly, so I had a script I could use to turn the screen off, but it turns out that, to turn on the screen, I have to send the "on" command, which is what happens when I tell the Switch entity that was created for the TV to turn itself on. And so, I had two scripts, one to call to turn on the screen, one to call to turn off the screen, which meant that I could then integrate turning the screen in the bedroom on and off using voice assistant commands (since the Raspberry Pi attached to that screen is also running a satellite instance of the voice assistant program).

So, I did succeed at my stated intent, although I can't say that any of that solution, nor the pathway I took to get there, would be considered anything in the realm of "elegant." But it works, and that's what counts.

Example 2: The Sensor Screensaver Bash-Together (in Python)

Flush from the success of getting the bedroom's devices set up and controllable the way that I wanted to, I turned my attention to a problem that one of the household had detailed for me earlier, and that I refreshed myself on.

The screen that I primarily use is connected to a Linux box that's my primary Dreamwidth browser, link-catcher, and so forth. The Windows box is mainly meant for multimedia and games, and has its own screen, and both of them are the Sirs Not Appearing In This Explanation, past the point that since it's the screen games are played on and the like, it's less likely to go into a screensaver mode than the Linux box's screen is.

As the problem was explained to me, since my screen is the one more likely to end up in a screensaver state, was there any way that the screensaver could be used to display data about the temperature and humidity of the rooms where the IR remote / sensor cables were located? (Yes, it's possible to get that data from the voice assistant, but if you're doing something that requires or uses sound, it can be a pain to mute the sound and then ask the question of the voice assistant. Or to try and be heard over the sound to get a proper question and response.)

I wasn't sure it was possible, but poking around in the back of my head was the knowledge that some of the various screensavers available in xscreensaver (the screensaver that I use) have the ability to showcase text like the hostname of the computer, its uptime, or other system information that updates in realtime while the screensaver runs. So, the first thing I did was examine xsceensaver's options, and it turns out that there are a couple different options for providing the text that can be inserted into the screensaver to be manipulated. System information could be offered, or an arbitrary line of text, or the text available at a particular URI could be retrieved and inserted into the screensaver (so if you pointed it at, say, Wikipedia's random article page, or TVTropes's Random Trope page, while the screensaver was running, it would grab the text served from the result of the random page option and then display it in the screensaver), or most interestingly for my purposes, you could get xscreensaver to run a command, which would then take the text output from that command and display it in the screensaver. (The first thing that sprung to mind was that it could run "fortune" with appropriate options, and therefore there would be something pulled from the fortune files and displayed.)

So xscreensaver could be pointed at a program or executable script and it would put the output of that program or script into certain screensaver modules. All I needed to do, then, was to craft the script that would provide the desired information. Except that my ability to code is a lot more "someone else has done this and I can modify it, right?" rather than "Hah! I shall write myself a delightful little program/library/script that gathers this information, parses it, and then produces some pretty text." So that meant it was research time. Sensibly (a rarity for me), I started with the Home Assistant integration that knows how to control and understand the sensor data from the cable attached to the IR remotes, which gave me a lead to the name of a Python library that had been written to handle most of that manufacturer's products, both for the IR functions and the sensor cable data. That's good. The library was available in PyPi so it could be installed and used, so that's also good.

The library itself is abysmally documented on GitHub as to what kinds of functions are available, what data types are returned when functions are called, and the function you have to call first, once you know what you're doing, before doing anything else. You can infer all of these things from the single sentences that are attached to the function examples, and the person who maintains the library also has two scripts, one that can be used to find the IR remotes on the wireless network and retrieve their useful data, and one that can be run from the command line to perform various functions or retrieve some of the data available from their sensor cables. They're both python scripts, and so I opened them up to examine what they might have available to them. I started with the discovery script, since it had an example of the temperature data call in it, and eventually figured out how to get the data structure into a variable and get the temperature data and the humidity data from the sensors to display. Which involved first finding out what the data structure of that variable was (a list) by improperly trying to manipulate it, which explained why the examples were structured the way they were, because I first needed to indicate which part of the list I wanted to work with. At which point I had to deal with a "your authorization has expired" error, which explained why the command that was marked as the one you had to run first was there, so that the sensor strip on the IR remote would actually give up the data payload.

At which point I got actual useful data out of the script! (w00t!) I compared it to the data returned by the Home Assistant to make sure that I was getting correct data, by doing the calculations necessary to convert the Celsius temperature to Fahrenheit. Those things matched! (w00t!) Which meant I then needed to do those calculations in the script itself so that it would display the data that I wanted. Which produced another error, because I was trying to do a operation reserved for numbers (ints and floats) on something I had declared to be a string, and Python took exception to it. So I had to rework the script. I generated a new variable that took the raw data, did the calculations, then ran the rounding function on it so as to have a nice number to display, which was ably assisted by the excellent Python documentation. Only after all of that had been done did I evaluate the variable and insert it into the print statement, at which point it was formatted into a string and the whole thing printed to the screen. A little more cleaning up, and checking to see how the final product would look in the screensavers themselves, and I had succeeded at solving the presented problem: taking the sensor data from the sensor cables of the IR remotes and displaying them in a useful form in a screensaver.

Total time spent from conception to completion for this sensor screensaver bash together in Python: approximately one evening, with a meal break in the middle. I'm surprised it works. I'm surprised I was able to put it together.

I should probably feel a lot more proud of this, or smart, or something resembling good feelings about this, but mostly I feel surprised that it works, and the feeling of "yes, this works, but someone who actually knew what they were doing would probably look at this and declare it ugly and in need of being done Right and Properly." Because, even though I know it's a load of bull, to the point where there are jokes about how actual coding is doing by looking at examples on stackoverflow (or something similar) and copying things until something works, I feel like all that I did was put stuff together and modify and iterate until it worked, which is apparently "not coding," because "coding" is apparently something that starts ex nihilo and then springs into being from the mind of the programmer / programming team. The hard parts (the python library and the xscreensaver text parsing) were already done, and all I did was figure out how to use the one to provide input to the other. You know, "just" creating the bridge script between the two, and even then, it was "just" figuring out how the examples worked and using them to do what I wanted to do.

You know, librarian stuff. The stuff that I have significant training, expertise, and a master's degree in. Which might be why I'm trying to dismiss it as unimportant or unimpressive, even though I'm usually a lot better about recognizing that librarian stuff is damn hard and requires skills. I did the thing, it works as intended. Even if I recognize its potential frailty and have no idea how to go about having the script discover the correct values, rather than having them hardcoded in, I still made the script, and it works.

And those are two examples of Adventures in Home Automation (and its related functions), both of which required skills, problem-solving, consultation of the documentation, and iterative design until they worked correctly. If nothing else, I hope they were entertaining stories.

Post a comment in response:

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