For Christmas, I received a Google Home and some Philips Hue lightbulbs. Within 15 minutes, I had every light in my apartment responding to voice controls! Naturally rather than accepting that life is awesome and that I live in the future, I was immediately annoyed that I still had to use a remote to change inputs on my TV. Here’s how I went about fixing that.
The Google Home is an interface to the Google Assistant, which currently only supports a integration through Actions on Google. Currently the only way to integrate as a random developer is to implement a Conversation Action, which means controlling my TV would sound a little bit like this:
“Ok Google, I’d like to talk to my TV” - Me
Home - “Here’s your TV!”
“Let’s play Wii” - Me
Home - “Setting your TV to Wii”
“Exit” - Me
Yikes, that’s not going to fly. Fortunately, Google has allowed IFTTT to implement Direct Actions, which let a developer specify special phrases to trigger an IFTTT recipe. Even more fortunately, IFTTT finally implemented a Maker channel, which allows arbitrary web requests. With that part sorted, let’s look at how we can control the TV.
I ordered a USB IR blaster from IguanaWorks, and jumped at the chance to use one of my fancy new Raspberry Pi 3s. I loaded up a fresh copy of Raspbian, installed LIRC from the repository, copied in an LG remote profile from this list, and tried my first command:
$ irsend send_once lg KEY_POWER
irsend: hardware does not support sending
Hmm. It turns out there’s quite a process left here, as detailed in the IguanaWorks Getting Started page. I had to build LIRC from source including the iguanaIR driver, and configure my hardware.conf. However with that finished, and the end of my IR blaster artfully taped over my TV’s IR sensor, I could toggle the TV power!
So let’s take stock here: We have a raspberry pi that can trigger IR commands on my TV, and the ability to call a website with a voice command. Clearly we’re missing some glue here.At this point there are many ways to solve the problem, the easiest of which is to open a hole in my router, and let IFTTT call my raspberry pi directly. Since I’m a glutton for punishment though, and have been interested in AWS for a while, lets try to solve this problem with AWS IoT.
The plan is simple. I’ll set up an AWS IoT device to represent my TV, and have my raspberry pi subscribe to the device state, and update the actual TV whenever the state changes on AWS. Then I can have Amazon API Gateway trigger a lambda function to trigger updates to the AWS IoT device.
AWS IoT
Configuring devices in AWS IoT is actually deceptively simple for home use. Amazon pre-defines tons of types with pre-defined attributes, but for my purposes, I really just want to track the current input and the current power state, so I created my own Thing without a type. I created two attributes for input and power.
Once the Thing (I called mine TV) is created, it will be visible on the AWS IoT dashboard.
AWS IoT manages the online state of objects through what it calls Shadows. The shadow represents the most recent online state, which the device will update itself to reflect at the next online check-in. In order to update an IoT shadow from a lambda function, you must grant the lambda function a special IAM permission. I used the following inline IAM code to allow my lambda function to publish to my Amazon IoT devices:
And the following python code is my lambda function itself. It contains a mapping from devices to inputs, and updates the input and power state accordingly. In addition, this lambda function takes a password key, that it requires before updating any IoT state. This lambda function is going to be publicly accessible, and I don’t really want strangers fiddling with my TV!
With that set up in AWS Lambda, it’s fairly trivial to set up an API gateway proxy. Just ensure to deploy your API to make sure that your changes are publicly visible!
IFTTT
From there, I set up an IFTTT applet that listens for the magic words “let’s watch $”, “let’s play $”, or “turn the TV to $”. It fires a request at my API, which updates my IoT shadow.
Setting up the Pi
So, we could carefully try to track the current state, and send the right number of power toggles and input selects, but that sounds like a recipe for disaster. Although they aren’t on your remote control, most TVs support idempotent IR commands like POWER_ON, POWER_OFF, and INPUT_HDMI1. The trick is simply finding them! I downloaded the manual for my tv from LG.com, and read the section on IR codes for my TV. I learned 2 things:
- My TV definitely supports idempotent IR commands.
- There is no way I’ll be able to interpret these codes.
Enter the internet. A post on RemoteCentral.com contains raw IR codes for all of the commands I need, and IRScrutinizer can read these codes and output them for LIRC. If you, like me, have an LG TV from 2000-2013, these codes should work for you.
With this setup, I can now update my TV to a known state, even if I don’t know the current state. Win. While I was testing though, I noticed that my TV will only respond to POWER requests while in standby. If I want to change the input, I have to wait until the TV is on. However, if the TV is already on, I’d like to change inputs immediately. So, when selecting for example, HDMI1, I send the following command:
Powering off is easier, I just send $ irsend send_once lg POWER_OFF
Finally, the Pi needs to be connected to AWS IoT. The AWS IoT system will build you a custom python SDK for each device, complete with access keys. I downloaded that, and then wrote the following small script to actually send the IR codes.
The full file is available for download: tvListener.py
And there we have it! An end-to-end system for controlling my dumb LG TV with a Google Home. I’ve been using this for a little over a week, and it’s working great, though there is occasionally a multi-second delay before the TV responds. If you have any recommendations for how I should change things, let me know in the comments below, or contact me.