Automating my Home with HomePod, Raspberry Pi and Node.
jsChris HawkinsBlockedUnblockFollowFollowingJan 28In my home I have Apple HomePod, which allows me to control parts of my apartment (like my Hue lights) by calling on Siri from anywhere in my studio (or outside the room, through my phone).
Despite my initial skepticism about voice commands, born from the awkwardness of talking to a machine, I got into the habit of using them.
Since my Hue lights have no physical switch and the app is several taps away, Siri rapidly became my main way of turning the lights on and off (alongside her other main function: setting timers).
As I became more accustomed to using voice commands I actually started to become frustrated with the things that I couldn’t do with Siri, like control my television or the Xbox.
While messing around with my TV’s settings I discovered Simple IP Control — a method of controlling my Sony Bravia by sending commands over a TCP connection.
ContentsBecause this is kind of a long one and not all sections will be equally relevant to all readers, here’s what you’re in for:An Introduction to Siri CustomisationControlling a Sony Bravia TVTurning on the Xbox OneSetting up Siri ShortcutsBringing it all TogetherFurther Reading and IdeasAn Introduction to Siri CustomisationIn late 2018, Apple made the Shortcuts app available for all iOS users.
With Shortcuts, you can create workflows to automate your phone or home in simple ways without writing code.
Shortcuts has a bunch of prebuilt commands for useful actions like sending messages or retrieving calendar events.
What it lacks is anything as low level as sending a string of TCP commands, but there is a mechanism for hitting a URL.
It is also possible to write your own Intents in Objective-C or Swift, but I decided not to explore this route in case I wanted to swap out HomePod for another assistant in future, or branch out into applications that weren’t driven entirely by voice.
Instead, I set about writing a web application that would respond to Siri-initiated commands through a set of simple URLs.
Controlling a Sony Bravia TVArmed with a manual of possible commands for my television, I built a Node.
js Express app (Github) that could respond to some of my common commands.
I started with the basics: power and volume.
The setPowerStatus command does what we need.
A Simple IP Control command for my Bravia is formatted as follows:The header consists of the characters * and S, which are static and the same for all commands.
The third byte (C) stands for Command.
There are four values that can occupy this position.
C for Command (command send to the TV), E for Enquiry (an enquiry sent to the TV to ascertain the value of a certain property — e.
what is the current volume?), A for Answer (sent back from the TV in response to Commands and Enquiries) and N for Notify (for events that are broadcast over the channel and notify the listener whether the volume is being turned up or down or whether the TV has been turned off.
One challenge I uncovered was that this style of communication (over a single TCP connection) made it difficult to write clean promise-based code.
Instead of using a pre-existing abstraction (like an HTTP request) I was stuck with writing to the channel, waiting and then resolving my promise when I got the response back.
This created some frustrating edge-cases: what if I sent two commands in quick succession?.How would I know which response was associated with which command?It was then that I stumbled upon Sony’s JSON-RPC documentation.
As much as I was having fun with the low-level TCP stream, the request/response nature of JSON-RPC over HTTP meant much less boilerplate code and a simpler implementation overall.
Using the JSON-RPC API was simple.
Given a service (like system), a command (like getPowerStatus) and parameters (in this example: true or false), a simple HTTP request could be constructed and sent to a URL on the TV.
Authentication with the TV is performed here with a pre-shared key sent in the HTTP request header.
There’s an alternative and frankly much more secure way of doing this by using the accessControl service.
This service allows a command to be sent to the TV and a code exchanged securely with HTTP Basic authentication.
Once that has been done, further authorisation is performed by a cookie.
Since my TV and application sit behind the firewall of my home router, I felt comfortable with using pre-shared keys for my use case, but your mileage may vary.
Turning on the Xbox OneThe Xbox is a different beast to the TV.
Microsoft didn’t seem to see fit to leave a nifty, always-on REST API lying around, so we’re stuck with what essentially amounts to a magic UDP packet.
js has the dgram module available out of the box for all things UDP.
I wrapped that in a Promise-friendly API as follows:The payload we send to the Xbox is assembled based on the Xbox’s live device ID, which is available from it’s settings.
If you want to simply clone my repository, you only need to specify the device ID in the config.
Setting up Siri ShortcutsIn order for Siri to be able to perform the actions I had created, she needed a consistent endpoint to hit.
Since I didn’t want my desktop PC humming away constantly and couldn’t rely on my laptop always being in the apartment, I decided to invest in a cheap new addition to the household.
Raspberry Pis are inexpensive, low-power PCs that run (by default) Raspbian, a Linux distribution optimised for their profile.
I purchased a Raspberry Pi 3 Model B+ which supports WiFi out of the box.
Raspbian has a full GUI for configuration, but once I had it connected to WiFi I unplugged the monitor and continued all my work over SSH.
To ensure the web application was always-on, I set up a socket activated service in systemd so that if the Node.
js process ever crashed it would be restarted when it was needed next.
Adding the Shortcuts to Siri was the easiest part of the entire exercise.
The Shortcuts app is very intuitive, and supports recording voice commands.
It works with HomePod out of the box.
After everything we did to get here, the Siri Shortcuts app is almost ridiculously intuitive.
Bringing it all TogetherSince my TV is an Android TV, it supports a range of Android TV applications like Netflix and YouTube.
Throughout this project, I built commands to launch those apps as well as to control volume and power and to pause and play content.
You can see all the different endpoints I created for my TV here.
I also tried to modularise the project so it would be easy enough to add support for TVs with different APIs.
One of the nice things about building simple APIs around the components in my home is how easy it is to compose them into something even more powerful.
With the utilities we’ve now built, it is straightforward to build an endpoint (and therefore a Siri command) that would turn on the Xbox, turn on the TV and then switch the TV to the correct HDMI 1 input.
And here is everything working as intended:Further Reading and IdeasWith the addition of the Hue lights, we can get even more clever than this (e.
lowering the lights to reduce glare).
Philips has simple API for this that they publish online here.
Among the assistants, Siri’s customisation is quite limited.
Alexa offers a sophisticated API with far more options, including variable inputs and branching commands.
If for no other reason than not having to hear “running your shortcut” every time you use a custom command, Alexa could be worth a look.
It’s great to get back into writing.
Hopefully this is the start of a new series of posts focused on random tech and more projects like this one.
Follow me for more.