Automating the Information Centre
|My kitchen info display. Now with a new and improved two-column calendar!|
In my last post I talked about setting up an information display in my kitchen. I left off with some musing thoughts about having the information centre automatically turn itself on in the morning. In my endeavours to do exactly that, it led me down quite the rabbit hole of problems I didn't anticipate.
Getting it to turn on and off on its own was fairly straightforward. A capability of pretty much any modern TV is its ability to be controlled via commands send over the HDMI cable. This is known as Consumer Electronics Control, or CEC. If you have a 4th generation Apple TV, you've no doubt seen this in action. When you click any button on the Apple remote, the TV is automatically turned on and switched over to the right input.
Since the Raspberry Pi connects over HDMI to the TV, it has the ability to send such commands, with the use of a library called libCEC. Installing it is dead simple -- there is a guide specifically for compiling and installing it on a Raspberry Pi. Deciphering how to use it, however, is a different ball game altogether...
The library comes with a command line utility called cec-client. This is used to pass certain commands to your various devices connected via HDMI. In my case, all I really cared about was the TV. I wanted to be able to send commands to turn it on and off, switch inputs, and query it for status.
Turning the TV on and off is quite simple. There are shortcuts for these events:
echo on 0 | cec-client -s -d 1
echo standby 0 | cec-client -s -d 1
Most cec-client shortcut commands have this kind of format:
echo command target | cec-client options
The above commands seem pretty straightforward. Device 0 is the TV, and the commands are on and standby. Makes perfect sense. The options at the end mean send a single command and close (-s) and brief feedback instead of verbose (-d 1).
Except... these didn't seem to have any effect... OK let's check the TV's menu to see if CEC needs to be enabled... oh yep! There it is! OK enabled, and let's try again.
At this point I'm beginning to think I'm done before I even get started... but there was one thing left to try... Beside the setting to enable CEC, there was also a setting to enable the Audio Return Channel. This is typically used to feed audio from the TV back to, say, an A/V receiver over the HDMI cable. For some bizarre reason enabling this setting did the trick! Now when I issue the above commands, the TV turns on and off.
Yay! First hurdle overcome.
Alas, switching inputs is a little more of a... shall we say... dark art... There is no shortcut command for this, so we need to get our hands a little dirtier... With the help of a site called CEC-O-Matic and copious amounts of Googling, I learned these two commands to switch inputs:
Switch to HDMI 1:
echo "tx 1F:82:10:00" | cec-client -s -d 1
Switch to HDMI 2:
echo "tx 1F:82:20:00" | cec-client -s -d 1
What the heck is this? This is what a CEC command actually looks like. the letters tx means "transmit." The following 8 digits carry the command signal. The first two digits -- 1F -- indicate the source of the command and the target. 1 is the Raspberry Pi, and from above we know that 0 is the TV. Except here, 0 didn't work. Instead I needed to use F which means "broadcast." Not exactly sure why that is, but that's what worked... Remember what I said about this being a dark art?
The next two digits -- 82 -- indicate a command to change routing (in English, switch inputs). The last four digits -- 10:00 and 20:00 -- indicate the actual inputs HDMI 1 and HDMI 2.
Great! So it should be a breeze to get status right? Well... not exactly... I can get the power status of the TV (e.g. ON or STANDBY). But determining which input is currently active on the TV is not easy. In fact, it proved impossible. Some of this is my misunderstanding of how CEC works, and some of it is having a non-CEC aware device on the HDMI bus (That's the Apple TV... it's a 2nd generation which did't include any CEC features). In order to determine which input is active, you actually need to query for the active device attached to that input. So if I ask the TV for it's active source, it can only respond positively for the Raspberry Pi on HDMI 2. The Apple TV on HDMI 1 is not CEC aware, so it cannot report itself as active. When the TV is switched to HDMI 1, I get the response "Unknown" for the active device.
OK. We can work with that right? If the active device is the Raspberry Pi, then I can assume HDMI 2 was the active source, otherwise if the response is "Unknown," then I can assume HDMI 1...
Well this is where things start to get weird... It turns out that in order for The Raspberry Pi to report itself as active, it needs to explicitly SET itself to active... so if the TV was first on HDMI, then switched to HDMI 2, it would be possible to still report unknown device...
My original plan was to have the TV turn on automatically in the morning, but before doing so, it would save the state of the TV so that it could restore it later when the TV automatically turned off. I'm using a Harmony Remote Control, and anyone who has used one knows that it gets all out of sorts when a device's input is switched independently of the Harmony. Ideally I was trying to make it so that when someone picked up the Harmony remote to turn on the TV, everything would still be in sync.
"But!" you say, "Harmony has a Help button to fix that kind of mess!" Indeed it does, but to compound the issue, when the TV switches inputs, it's the kind where you click the "Sources" button on the remote and arrow up or down to pick the input, then press OK. The Harmony has a real hard time with this when it gets out of sync. In order to put the TV back to the right input, it needs to know which wrong input it is on. It tries its best to guess, but it seldom gets it right.
At this point, I was able to set up a schedule in Raspbian's cron to have the TV turn on in the morning, and turn off again at 10:00 AM. On the weekends, it's set to stay on a bit longer so that we'll know what the weather is going to be like or if there are any transit alerts we need to know of. The project is half complete, and if we never watch the Apple TV, then the remote never gets out of sync...
Yeah I know... you can almost feel my OCD kicking in, can't you?
I got to thinking... I already had at my disposal 4 discrete commands: TV on, TV off, switch to HDMI 1, and switch to HDMI 2. What if I could have my Harmony remote trigger those commands on the Raspberry Pi instead of interacting directly with the TV?
And thus commenced Phase II of this project! I would need a few things for this:
- An IR receiver connected to the Raspberry Pi
- An old remote control that no longer has a matching piece of gear
- Software on the Raspberry Pi to receive the IR commands and trigger the CEC commands
- The will to fight through the eventual turmoil I wish I had known awaited me...
In any case, I forged ahead trying to roll my own receiver.
I ordered one of these from ModMyPi.com. Seemed pretty straight forward... three leads. One for power, one for ground, and one to output the signal it receives from an IR remote control. These get wired to the GPIO on Pin 3 (3v3 power supply), Pin 6 (Ground) and Pin 12 (GPIO 18) respectively. (Why the pin numbers and GPIO numbers are different I'll never understand...)
See now the one wrinkle I didn't see coming was its incompatibility with the latest version of Raspbian OS. The other was the fact that in the latest version of LIRC, things had changed so much with regards to its setup, that none of the tutorials were up to date.
I ran into roadblock after roadblock, and just when I thought I was going to finally get passed the last hurdle I ran into something that I couldn't fix.
Configuring LIRC is a three step process:
- Configure your hardware (i.e.: the IR Receiver) and verify it can receive a signal.
- Record the key presses from your remote control so that LIRC can learn your remote.
- Map key presses to actions so that the key presses do something useful.
The first step went off without a hitch. I ran the command to test that I had everything wired up correctly and I could see raw data being captured by the IR sensor.
The second step took a LOT of fiddling with things before I finally got it to work. By default the driver is set to something that doesn't actually work. And to run the program that records the remote keys, you need to stop the LIRC service. Took me FOREVER to figure that out (the name of the service changed between versions, but it's not documented anywhere...).
But here is where I hit the brick wall. Once the config file for the remote was generated, I still couldn't get LIRC to recognize any key presses. It turns out that in the latest version of Raspbian OS, LIRC generates configuration files that don't actually work. What's worse, is that in my Googling to try and solve this, I only came across one thread that discussed this, with no resolution. LIRC has not been fixed to address this, and that thread is several months old. I'm not holding my breath that this gets fixed anytime soon...
So if I wanted to get this to work, I had no choice but to use the previous version of Raspbian OS. Much like Apple devices, there's no easy way to downgrade to a previous version of the OS. I needed to reformat the card, install the old OS, and go through the whole process of getting everything back to the way it was. That meant reinstalling Magic Mirror, downloading and configuring all of the modules, adding back my custom CSS, reinstalling libCEC, customizing the Raspbian Desktop to suit my needs, etc., etc., etc.
That was a good waste of an entire evening...
BUT! I'm happy to say that this was the turning point that indeed made everything work! Now that I was on the previous OS version, all of the tutorials were correct, and I had LIRC up and running in no time at all!
I had mapped buttons 1-4 on the universal remote to be TV On, TV Off, HDMI 1, and HDMI 2 respectively. Pressing those buttons on the remote caused the Raspberry Pi to control the TV as expected. All that was left was to program the Harmony Remote with the new commands.
If you've ever used a Harmony Remote, you'll know that programming it is largely automatic. You tell it the make and model of your device, and the software combs through its vast library of IR codes to make sure you get the right ones programmed in. But if you dig into it, you also get quite a lot of control over customization. Admittedly, Harmony's configuration software is junk -- It's a Silverlight app!!! -- and it doesn't serve the advanced user very well, but if you're willing to put up with the hoops it makes you jump through, you can change all sorts of things.
In my case, I changed what Harmony thought it knew about my TV. Originally, the TV worked with a single power toggle command. Press it once, and the TV turns on, press it again and the TV turns off. I instead told the Harmony app that in fact my TV uses two distinct keys for power, and I was able to teach these to the Harmony remote using the universal. Similarly, I changed the the behaviour of input switching from the previous Source -> Arrows -> OK behaviour to behave as if the remote had dedicated keys for the inputs.
Now everything works as expected! It doesn't matter what the previous state of the system was... when I press an activity key on the remote, it uses the explicit commands to turn the TV on and switch it to the specific input. The remote will never get out of sync!
I mounted the IR receiver on the top-right corner of the TV:
It's hard to tell from that photo, but it's really tiny. Here's another photo of the whole TV. Notice the little knob sticking up in the top right. Keep in mind this is a 32" TV, i that gives you some idea of scale.
So now it's ALMOST perfect... There's one niggly thing I'd like to fix at some point. The way I'm using libCEC, each time I issue a command, a connection to the adapter is opened, the command is issued, and then the connection closes. It takes a second or two for the connection to open, so the responsiveness of pressing the button on the remote is a little slow. I'd like to figure out how the have the connection to the adapter remain open, and then issue commands ad hoc when I need to. Changes to the TV's state happen instantaneously when the command is actually issued.
But I need a break for a few days... this was frustrating, and far more difficult than it should have been. I'm happy that it eventually worked out, and truth be told, usually I enjoy the journey more than the destination. In this case, though, I'm going to wait a couple of weeks or so before I come back to try and perfect this.