Television with HA progress

Hey all!

I wanted to share with you my goals and setup attempts and what I ended up with. I got an Echo Dot for Christmas and as a result I’ve been having fun with Alexa commands and what I can get away with doing hands-free. This lead me to purchase a Harmony Hub and trying to tie the Harmony skill together with Alexa. I should have known better, given prior experience with Harmony remotes forcing me to use “Activities” and the limited customization options you have in their app. Its seemed that not much has changed with the Hub, but nonetheless, I’ve overcome some limitations.

One limitation was I hated that I could not say “Alexa, pause the TV.” I know I could do simple ones like “Alexa, turn on/off the TV” but beyond those, you’d have to say “Alexa, ask Harmony to X/Y/Z/”. To me, that’s clunky and it was a bit sluggish.

Also, Harmony activities get easily out of sync when Televisions get turned off manually, or when they go into standby mode on their own. Because Harmony app will not let me add/remove certain power on/off device commands in an attempt to use it, I abandoned using Harmony App to control TV state or input state, and pushing down simple commands to HA. I’ve also managed to solve my TV state issue along the way.

My environment:

  • HA running in a Ubuntu Server VM hosted in ESXi.
  • Raspberry Pi 3 purchased later running Retro Pie connected to my TV (important in a moment)
  • Harmony Hub

My goal was to remove the harmony remote platform from my HA config and instead, find a way to make command line switches to Turn on or off the TV, regardless of what state Harmony “thought” the TV was in.

I ended up downloading a node package called harmonyHubCLI that did just that

(https://www.npmjs.com/package/harmonyHubCLI)

switch:
      back_room_tv:
        friendly_name: Back Room TV
        command_on: "node /usr/lib/node_modules/harmonyHubCLI/harmonyHubCli.js -l 192.168.1.20 -d 'Vizio TV' -c 'PowerOn'"
        command_off: "node /usr/lib/node_modules/harmonyHubCLI/harmonyHubCli.js -l 192.168.1.20 -d 'Vizio TV' -c 'PowerOff'"

As you can discern, this package can support multiple harmony hubs via IP address and send specific commands to the device of your choosing. So while I could have gotten an IR blaster and scrounged up IR Codes, since I’d already bought this thing, I’m able to still use the “good” parts of the Harmony Hub…

This left me with a switch that I could always hit “on” or always hit “off” to send the codes. However, I was unable to determine state reliably, but at least send the commands. Around that time is then when I grabbed a Raspberry Pi 3 for fun and hooked it up to the same thing. I started reading about hdmi_cec on HA, and while I could technically run that on the Pi instead of my VM, I didn’t feel like migrating everything over, and would rather the Pi just be an emulator appliance… This meant I couldn’t natively use the hdmi_cec, however I ended up writing a ssh script:

#!/bin/bash

status=`ssh [email protected] "echo 'pow 0' | cec-client -s -d 1" | grep "power status"`

echo "$status"

if [ "$status" == "power status: on" ]; then
  exit 0
else
  exit 1
fi

The only configuration I had to do on the Pi, was to install cec-client, and establish trusted ssh between my HA Ubuntu instance and the Pi. This quick and dirty script basically asks the Pi for the Power Status of the TV on the CEC chain so that it can determine state which I added to my yaml like this:

command_state: /home/mike/checkCec.sh

I should add that in conjunction with the Phillips Hue Emulator, I’m able to completely remove the Harmony Skill, and still be able to say “Alexa, turn off Back Room TV” just fine.

So I took this a step further, and wanted to be able to pause TV when I ran in the kitchen, so I made a switch called “Pause Den” so I can say “Alexa, turn on Pause Den” or “Alexa, turn off Pause Den”. The commands are similar to power on and off:

  pause_den:
    friendly_name: Pause Den TV
    command_on: "node /usr/lib/node_modules/harmonyHubCLI/harmonyHubCli.js -l 192.168.1.26 -d 'Xfinity DVR' -c 'Pause'"
    command_off: "node /usr/lib/node_modules/harmonyHubCLI/harmonyHubCli.js -l 192.168.1.26 -d 'Xfinity DVR' -c 'Play'"

Finally, I wanted to be able to tune to certain channels without using the Harmony skill or App, so I toyed with the input_select options and came up with this:

input_select:
  back_room_channels:
    name: Channels
    options:
      - -- Select --
      - NBC
      - CNN
      - ABC
      - TMC
    initial: -- Select --

  den_channels:
    name: Channels
    options:
      - -- Select --
      - NBC
      - CNN
      - ABC
      - TMC
    initial: -- Select --

Next, I made 2 shell_commands:

  back_room_channel: "/home/mike/changeChannel.sh {{states('input_select.back_room_channels')}} 192.168.1.20"

  den_channel: "/home/mike/changeChannel.sh {{states('input_select.den_channels')}} 192.168.1.26"

I then made an automation to fire the commands based on input selects changing:

  - alias: Change Channel Back Room
    trigger:
      - platform: state
        entity_id: input_select.back_room_channels
    action:
      service: shell_command.back_room_channel

  - alias: Change Channel Den
    trigger:
      - platform: state
        entity_id: input_select.den_channels
    action:
      service: shell_command.den_channel

And this is what my amateur shell script looks like:

#!/bin/bash

channel=$1
hub=$2
commands=[

if [ "$channel" == "NBC" ]; then channel=805; fi
if [ "$channel" == "ABC" ]; then channel=803; fi
if [ "$channel" == "CNN" ]; then channel=849; fi
if [ "$channel" == "AMC" ]; then channel=825; fi

for (( i=0; i<${#channel}; i++ )); do
  digit="${channel:$i:1}"
  commands=$commands$sep\"$digit\"
  sep=,
done
commands=$commands]
if [ "$channel" != "-- Select --" ]; then

echo $commands
echo "Switching to channel $channel"
# node /usr/lib/node_modules/harmonyHubCLI/harmonyHubCli.js -l "$hub" -d 'Xfinity DVR' -c 'Exit'

node /usr/lib/node_modules/harmonyHubCLI/harmonyHubCli.js -l "$hub" -d 'Xfinity DVR' -c "$commands" -m >> /home/mike/channelLog.txt
node /usr/lib/node_modules/harmonyHubCLI/harmonyHubCli.js -l "$hub" -d 'Xfinity DVR' -c 'OK' >> /home/mike/channelLog.txt

echo Channel:$1 Host:$2 \
>> /home/mike/channelLog.txt

fi

So if you made it this far and wondered what the completed product looks like (along with some other junk), here’s what it looks like in HA, now:

PS, there’s a lot of other stuff going on in this tabs like Activating the HDMI inputs etc, but the concept is the same.

Hope this is helpful to folks!

5 Likes

Amazon Web Services issue. Probably just a temporary outage.

1 Like

Gotcha - I’ll try again in a bit!

Yeah, I also tend to avoid the activities altogether. I still need the activity to be on my “TV” activity so my remote will work as expected. (main controls my firetv, volume controls my sound bar etc). Also I use the state of the harmony remote in home assistant just so I can see the current activity for automations. (if activity is TV, then turn the light on behind the TV)

The cli commands seem useful, if you didn’t post this I would probably have just hit the rest api of home assistant to do harmony service calls in my scripts but i’ll check that should take out the middleman, even if my middleman is locally hosted.

Luckily though, since I mainly use a fire tv for my media I can use the python firetv https://github.com/happyleavesaoc/python-firetv adb tool to send commands to that instead of going through the harmony at all.

I’m rambling but overall, yep I agree, avoiding activities is the way to go for me too.

Well, speak of the devil. I saw a tweet by C|Net and here it is:
https://twitter.com/CNET/status/836660880880259072

And a link to the article:

1 Like

Worth noting the HDMI CEC support includes the ability to relay commands from your PI to your VM (so you don’t have to run HASS on your PI): https://github.com/konikvranik/pyCEC#running-server

My own TV setup is similar. I have a PI attached via HDMI that can switch inputs, volume, power, and then an IR receiver and transmitter on the PI’s pins so I can intercept my remote control, and of course send IR.

One useful automation I have is when I press a particular remote control button, it toggles the room’s lights.

@entmike Really a great write up. I was able to understand a good chunk of it. For me though, it would help to get a little more detail in which file some of these entries are made. I would have a similar use case as I want to open up my drapes (IR based) via Alexa. The commands are already in my Harmony hub so I would assume that the only thing that I would need to install to make this work is: the HubCLI package. For the rest, I’ll have to read it again several times so that I can digest it. Thanks for posting

1 Like

Thanks for the heads-up on the HDMI CEC relay over network. I’ll have to see if this would work with the hdmi_cec HASS platform using my quite limited Linux experience. (I know just enough to duct tape and bubblegum stuff together) - Do you know if pyCEC would make the HA hdmi_cec platform essential work in my configuration?

And wow, it never occurred to me to use the Pi to receive IR codes to control lights! (I’d always thought about it as a potential IR blaster, kinda the reverse…) That’s a pretty cool use case to re-purpose old unused IR remote controls!

Hey @berniebl, all configuration changes in my write-up are made to the configuration.yaml file, with the exception of the bash scripts being their own .sh files in my home directory. If you have any questions about any of the commands, just let me know!

I have a question; what is the device in the lower right hand corner? Looks like a media player but with a Windows 10 PC in the viewer.

I run ESXi (aka VMWare Vsphere) on 2 Intel NUC Skull Canyons for my home lab/VM farm where I’m running anywhere between 5-10 VMs at any point. One is my “main” home Server 2016 that runs DHCP and DNS and hosts a general-purpose SQL Server DB. The image points to the ESXi console, which is an image of whatever is happening on that VM (image refreshes about once every 10 seconds or so). It’s usually just sitting there at the logon screen and was more of a test to see if I could pull in a bunch of VM consoles at once, but then I just dialed it back to my main server VM for fun. It doesn’t really do anything besides verify my server is running :slight_smile:

EDIT: Related, in the screenshot, It also runs my iTunes for hosting to my Apple TVs, so I also have that command line switch to turn off and on iTunes when it mysteriously drops off my network (probably separate some Bonjour/router issue) - Basically I could make a bunch of command-line switches to stop/start services and VMs, etc. But baby steps for me :slight_smile:

1 Like

That’s neat and a great idea!

So do you have a script that does the screen cap and then just display it in generic camera?

Actually the screen cap is handled automagically in the ESXi web frontend out of the box. For instance for VM 11 in your server, the call from a browser is:

https://192.168.1.2/screen?id=11

This URL will require a basic authentication username password.

The problem I ran into with simply using the Generic IP camera directly, was that I couldn’t get it to pass basic authentication and https to that link.

So first attempt, I simply had a crontab job runnning a one-liner script every minute to write it out to a file:

curl --insecure -u root:mypassword https://192.168.1.2/screen?id=11 > /home/mike/.homeassistant/www/images/test.png

I didn’t like this approach and every once in a while, the “camera” would be reading the file at same moment it was writing it, and you’d get no image, so I changed to a poor man’s proxy approach and wrote a Node JS http server script to serve it up as a fake generic camera. (Ignore the traffic cam stuff in the script, it’s just left over junk from some other experiment that I’m not using actively):

var http = require('http');
var https = require('https');
var url = require('url');
const handleTraffic = function(request, response, query){
        //https://tnsnapshots.com/thumbs/R4_105.flv.png
 var options = {
    host: 'tnsnapshots.com',
    rejectUnauthorized: false,
    port: 443,
    path: '/thumbs/' + query.cam ,
    headers : {
      'Accept' : 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
      'Accept-Encoding' : 'gzip, deflate, sdch, br',
      'Accept-Language' : 'en-US,en;q=0.8',
      'Connection' : 'keep-alive',
      'Host' : 'tnsnapshots.com',
      'Pragma' : 'no-cache',
      'Referer' : 'https://smartway.tn.gov/traffic',
      'Cache-Control' : 'no-cache',
      'Upgrade-Insecure-Requests' : 1,
      'User-Agent' : 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87                                                                                                                         Safari/537.36'

    }
  };
  return options;
}
const handleVM = function(request, response, query){
 var host = '192.168.1.2';
 if(query.host !== undefined) host = query.host;
 var options = {
    host: host,
    rejectUnauthorized: false,
    port: 443,
    path: '/screen?id=' + query.vm,
    headers : {
      'Authorization': 'Basic ' + new Buffer("root" + ":" + "mypassword").toString("base64")
    }
  };
  return options;
}
const requestHandler = function(request, response, query) {
  var query = url.parse(request.url, true).query;
  //console.log(request.url);
  if(query.vm === undefined && query.cam === undefined) {
    response.end();
    return;
  }
  var options = {};
  if(query.vm !== undefined) options = handleVM(request, response, query);
  if(query.cam !==undefined) options = handleTraffic(request, response, query);
  var req = https.request(options, function(resp){
    resp.on('data', function(chunk){
      response.write(chunk);
      // console.log(chunk.toString('utf8'));
    })
    .on('end', function(){
      response.end();
    });
  }).on("error", function(e){
    // console.log("Got error: " + e.message);
  });
  req.write(JSON.stringify({}));
  req.end();
}
const server = http.createServer(requestHandler);
server.listen(8124, function(err){});

So my YAML entry for the VM “cameras” are simply:

camera:
  - platform: generic
    name: server2016
    still_image_url: http://127.0.0.1:8124/?vm=11
  - platform: generic
    name: ubuntu
    still_image_url: http://127.0.0.1:8124/?vm=31

Basically I pass a vm= parameter and the Node script relays the request to pass the properly formed authentication and respond with the payload as an image.

1 Like

I’d like to suggest that you move these last two replies into a separate Share Your Projects post; I see a number of other VM users here that could benefit from this and it’s worth showing it off.

Thanks for taking the time to explain your setup to me in such great detail. Users like yourself are what make this forum so valuable.

Yeah, I’ll need to take a little time to clean up that messy Node script so it’s meaningful for a new post. I’m somewhat of an anarchist and try all kinds of borderline “stupid-but-maybe-cool” stuff maybe others can use LOL :slight_smile:

1 Like

Oh great, you’ll fit right in with this merry band!! Keep up the experimenting!