smart tv less dumb

Quick Project: Making my Smart TV Less Dumb

This post may contain affiliate links. Please see the disclaimer for more information.

The word “smart” has become so loaded in these days of smart-this and smart-that. The question is what qualifies as “smart”? Surely the answer is some form of Internet connectivity? However, I’m not sure that goes far enough. I like to see some degree of interoperability with other systems before I call a device smart.

For me the other system in question is Home Assistant. I’m not going to call a device truly smart unless there is a way to make it integrate with HASS. Of course, HASS has a lot of integrations which should make it easy right?

Not so for my “smart” TV (a 2017 model Sony Bravia) which fits neatly into a chasm between Sony’s previous Bravia offerings and their Android TV line. As such the integration in HASS doesn’t work with it, because the service it relies on is not available on the TV and it’s not running the truly smart Android TV OS.

Sony just seem to have slapped Netflix, YouTube and a couple of other apps on the previous TV software, disabled the network control service (presumably because it didn’t work with those apps) and sold these as Smart. Seems pretty stupid to me. It’s a shame, because otherwise it’s a really nice TV. It’s high time I made this Smart TV less dumb.

Getting Smart

I’ve written previously about how I’ve integrated power switching for the TV into HASS via HDMI CEC and Node-RED. Well recently I’ve made a couple of improvements to this which have increased it’s utility and made my Smart TV slightly less dumb.

The first of these was to add a ping sensor to report on the power state of the TV. It seems like the TV has pretty rudimentary (but in my opinion good) power management, since it responds to pings when on and doesn’t when in standby.

Here’s the YAML for that sensor:

binary_sensor:
  - platform: ping
    name: "Living Room TV state"
    host: !secret living_room_tv_host
    count: 1
    scan_interval: 15

Basically this is just pinging once every 15s. I kept the repetitions this low in order to ping more frequently and still keep the network traffic low. If a ping is lost I’m not too worried since the next one happens pretty quickly. Also the TV is on a wired Ethernet connection, so packet loss is very low (as compared to wifi).

I’ve fused this with my existing power switch in order to overcome some reporting weirdness from the CEC switch. It seems to me that the TV doesn’t always report it’s power state, especially when turned off via the remote.

Here is the template switch that integrates the two:

switch:
  - platform: template
    switches:
      living_room_tv_fused:
        value_template: "{{ is_state('binary_sensor.living_room_tv_state', 'on') }}"
        turn_on:
          service: switch.turn_on
          data:
            entity_id: switch.living_room_tv
        turn_off:
          service: switch.turn_off
          data:
            entity_id: switch.living_room_tv

This switch becomes the primary means for controlling and displaying the TV power state in HASS. Now it’s really reliable, with the caveat that it may not update the state immediately, due to the ping interval. This is something which I can live with because it will now show the correct state most of the time. Previously it showed the incorrect state most of the time!

Detecting Netflix

As I was port scanning the TV (what? you mean it’s just me that scans every device that comes into my house?), I noticed an that port 9080 was open. Some investigation proved that this port is opened by the Netflix app on the TV and low and behold, if I exit Netflix the port is closed.

This gives us a nice way to detect if Netflix is running on the TV. This is useful, because despite my protestations people in my household seem to want to use the native app rather than Kodi (instability of the Kodi plugin is one reason for this). This is unfortunate in that I can’t see the state in HASS and therefore have had to disable my automation to turn off the TV when nothing is playing, lest it switch off five minutes into a Netflix stream.

I used the command line binary sensor to detect the open port (thanks to VDRainer on the HASS community forums for helping me with an issue here):

binary_sensor:
  - platform: command_line
    name: "Living Room Netflix"
    command: '/usr/bin/nmap -p9080 <insert ip address> | /bin/grep open > /dev/null && (echo ON) || (echo OFF)'
    device_class: connectivity

Here we run an nmap command which will detect the open port, grep for the answer and output ON or OFF depending on the state. By default this will be run every minute, which is good enough for me. Unfortunately, I haven’t found a way to use secrets with the command, so you’ll need to insert the IP of the TV manually if you want to use this.

smart tv less dumb
The Netflix Sensor

This doesn’t give us any information on whether or what Netflix is playing, just that the app is open. That information is probably available via this port, since it seems to be running some kind of web service for remote control. Someone would need to do the relevant investigation to work out what the endpoints are though.

Putting It All Together

Now I can update my TV power off automation:

  - alias: 'Turn off TV if idle for 5 minutes'
    trigger:
      - platform: state
        entity_id: media_player.living_room_kodi
        to: idle
        for:
          minutes: 5
      - platform: state
        entity_id: binary_sensor.living_room_netflix
        to: 'off'
        for:
          minutes: 5
    condition:
      condition: and
      conditions:
        - condition: state
          entity_id: media_player.living_room_kodi
          state: idle
        - condition: state
          entity_id: binary_sensor.living_room_netflix
          state: 'off'
        - condition: time
          after: '20:00:00'
          before: '07:00:00'
    action:
      - service: switch.turn_off
        data:
          entity_id: switch.living_room_tv_fused
      - condition: and
        conditions:
          - condition: state
            entity_id: light.living_room_spots
            state: 'on'
          - condition: template
            value_template: '{{ states.light.living_room_spots.attributes.rgb_color == [0, 0, 255] or states.light.living_room_spots.attributes.rgb_color == (0, 0, 255) }}'
      - service: light.turn_off
        data:
          entity_id: light.living_room_spots

Here I’m triggering the automation if either the local Kodi player or the Netflix sensor are idle/off for 5 minutes. I also check that both of these are in the desired state in the conditions. The extra time condition is to account for the use of the TV YouTube app, which I can’t detect. It only tends to be used before 8pm, so I restrict the time that this automation runs just in case.

Conclusion

That actually turned out to be quite a long post for a ‘quick’ project! I’m pretty happy with this approach and it’s allowed me to bring back an automation which has been disabled for ages. Hopefully this can start saving some power again, since people still leave the TV on. Now if only I could detect that pesky YouTube app! Then I really will have made my smart TV less dumb, but not yet truly smart (at least as far as I’m concerned).

If you liked this post and want to see more, please consider subscribing to the mailing list (below) or the RSS feed. You can also follow me on Twitter. If you want to show your appreciation, feel free to buy me a coffee.

Multi-Room Audio Client

Multi-Room Audio System: Indoor and Outdoor Audio with Snapcast and Mopidy

This post may contain affiliate links. Please see the disclaimer for more information.

One of the projects I really wanted to do when moving into our new house was build a multi-room audio system. Traditional multi-room audio systems, such as Sonos, cost a massive amount for the functionality they provide. It looks like a cheaper alternative is now available using Chromecasts, but you are still at the mercy of what the manufacturer wants to do (like discontinuing the Chromecast Audio).

In this post I’m going to detail my multi-room audio setup, which plays perfectly synced audio across three sets of speakers, both indoors and outdoors. This system is 100% DIY and uses Free Software throughout. It’s also cheaper than even a single Sonos speaker.

System Overview

My system is comprised of a central server running Mopidy and Snapcast (the snapserver portion) and three audio players, each running the Snapcast client (snapclient).

Two of the clients are resident on the Raspberry Pi systems we use for Kodi on our TVs. One of these is located in the Living Room and connects into our soundbar. The other is located in the master bedroom and currently just uses the TV speakers.

The third client is located on a Raspberry Pi in our loft space, which is connected via an amplifier to speakers mounted outdoors by our patio.

The parts list for this setup is as follows:

The Fusion speakers listed above are outdoor/marine rated and certainly seem fine in the New Zealand climate (warm humid summers, wet cool winters). They are definitely not the best speakers in the world (the price reflects that). However, the quality is sufficient for my application of background/work music in an outdoor environment.

Overall, the total cost for the components ordered for this project was less than NZ$250. This comes in at less than the price of a single Sonos speaker. I’ve not included the Raspberry Pis in this, since I already had them and only one was specifically installed for this project.

Software Setup

The software setup is a pretty standard for this kind of project – basically just Mopidy feeding audio to Snapcast. As such I’m not going to give a full installation guide, since there are plenty of resources available. Take a look at the links below for full instructions (these are the resources I used when setting this up):

Multi-Room Audio UI
The Web UI Via Iris

In terms of client/remote control software, I’m using Iris as a web interface for Mopidy. On the Android side I’m using M.A.L.P. as well as the Snapcast app. M.A.L.P. seems to be a reasonable MPD client and supports multiple servers, which may come in useful in future. The main issue I have with it is that it gets the album art wrong frequently and there seems to be no way to override it’s choices (or use the correct album art from the server).

Of course, I also have both Mopidy and Snapcast integrated with Home Assistant!

Outdoor Speaker Hardware Setup

So far, so easy. Here is where I ran into issues. I mounted the speakers to the brick wall of our house just fine, but ran into problems running the cables up through the roof space to the amplifier. This was mainly due to one speaker being on the corner of the house where the roof is low. In this corner the steel supports for the roof were too close together for me to squeeze through. Also the level of the soffit where the cable came in was lower than ceiling height, so that the soffit forms a well around the outside of the house. All this made it nearly impossible to grab the cable.

Multi-Room Audio Speakers
Left Speaker (the one with the tricky cable)
Multi-Room Audio Speakers
Right Speaker

I fashioned a makeshift tool from an old mop handle and reacher grabber with a line attached to the handle so that I could actuate it from the end of the pole. I even went as far as installing the Android IP Webcam app on an old phone and mounting that on the far end. With this I could then view the image on my phone and use the light on the camera end to see better. This helped, until the battery on the phone died! Eventually I managed to grab the cable by pushing the whole length of it up through the soffit. The resulting bundle was much easier to grab.

Phew, now we’re getting somewhere…

Overall, getting the speakers installed took most of a day, with several hours spent laying on my front in the (hot) loft space trying to grab the cable. The provided speaker cables also had to be lengthened with some extra speaker cable from my local DIY store. Luckily I knew this before I installed them and didn’t have to pull them back.

Multi-Room Audio Client
The loft RPi with relays, sitting on top of the amplifier
Multi-Room Audio Client
The LED on the amplifier is unnecessarily bright

The remainder of the install was pretty much plug and play. I connected one of the USB soundcards to the Raspberry Pi and connected it’s output via audio cable to the amplifier. I spliced the relay into the 12V power line from the power supply to the amplifier to allow me to remotely control it’s power. Both the RPi and the amp are powered from the mains sockets I previously had installed in the loft.

Node-RED Relay Control

Multi-Room Audio Power Control
Power Control is done via Home Assistant

As with the relay power control for my room sensors, I used Node-RED to turn the relay on and off via MQTT. The flow uses my Home Assistant MQTT Discovery approach to be automatically added to HASS. There’s not much to say about this since it’s pretty much identical to the setup for the room sensors. Here’s the flow:

Multi-Room Audio Power Control
The relay power control flow

And here’s the corresponding JSON:

[{"id":"cd2cc1ba.21df68","type":"rpi-gpio out","z":"47863444.0972b4","name":"Relay 1","pin":"11","set":true,"level":"0","freq":"","out":"out","x":740,"y":60,"wires":[]},{"id":"b3084312.29b44","type":"mqtt in","z":"47863444.0972b4","name":"Outdoor Speakers Command","topic":"homeassistant/switch/outdoor_speakers/cmd","qos":"2","broker":"65d3656f.217c14","x":170,"y":80,"wires":[["72ed2721.9aa348"]]},{"id":"72ed2721.9aa348","type":"switch","z":"47863444.0972b4","name":"On or Off?","property":"payload","propertyType":"msg","rules":[{"t":"eq","v":"ON","vt":"str"},{"t":"eq","v":"OFF","vt":"str"}],"checkall":"false","repair":false,"outputs":2,"x":410,"y":80,"wires":[["36e87336.07603c","545168e2.b9a558"],["32dd96e5.5737fa","545168e2.b9a558"]]},{"id":"36e87336.07603c","type":"change","z":"47863444.0972b4","name":"ON","rules":[{"t":"set","p":"payload","pt":"msg","to":"1","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":590,"y":60,"wires":[["cd2cc1ba.21df68"]]},{"id":"32dd96e5.5737fa","type":"change","z":"47863444.0972b4","name":"OFF","rules":[{"t":"set","p":"payload","pt":"msg","to":"0","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":590,"y":100,"wires":[["cd2cc1ba.21df68"]]},{"id":"636d5fdd.480fc8","type":"mqtt out","z":"47863444.0972b4","name":"Send Messages","topic":"","qos":"2","retain":"true","broker":"65d3656f.217c14","x":730,"y":240,"wires":[]},{"id":"946d297a.c325","type":"function","z":"47863444.0972b4","name":"Format config messages","func":"var config = {\n    payload: {\n        name: \"Outdoor Speakers\",\n        command_topic: \"homeassistant/switch/outdoor_speakers/cmd\",\n    },\n    topic: \"homeassistant/switch/outdoor_speakers/config\"\n};\nreturn config;","outputs":1,"noerr":0,"x":390,"y":240,"wires":[["636d5fdd.480fc8"]]},{"id":"1f888f96.a08ec","type":"inject","z":"47863444.0972b4","name":"@Startup","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":true,"onceDelay":"3","x":130,"y":240,"wires":[["946d297a.c325"]]},{"id":"545168e2.b9a558","type":"function","z":"47863444.0972b4","name":"Set topic","func":"msg.topic = \"homeassistant/switch/outdoor_speakers/state\";\nreturn msg;","outputs":1,"noerr":0,"x":600,"y":160,"wires":[["636d5fdd.480fc8"]]},{"id":"65d3656f.217c14","type":"mqtt-broker","z":"","name":"MQTT Broker","broker":"mqtt.example.com","port":"1883","clientid":"","usetls":false,"compatmode":true,"keepalive":"60","cleansession":true,"birthTopic":"nodered/loft/status","birthQos":"2","birthRetain":"true","birthPayload":"online","closeTopic":"nodered/loft/status","closeQos":"2","closeRetain":"true","closePayload":"offline","willTopic":"nodered/loft/status","willQos":"2","willRetain":"true","willPayload":"offline"}]

I also have a couple of automations which I use to mute/unmute the relevant Snapclient when the speakers are turned off. My completely unfounded hypothesis is that Snapcast should be intelligent enough to not send any data to muted clients, which should reduce unnecessary traffic on the network. I’ve not done any investigation to verify this however. In any case, here are the automations:

automation:
  - alias: "Mute outdoor speakers when powered down"
    trigger:
      platform: state
      entity_id: switch.outdoor_speakers
      from: "on"
      to: "off"
    action:
      service: media_player.volume_mute
      data:
        entity_id: media_player.snapcast_client_outdoor_speakers
        is_volume_muted: true

  - alias: "Unmute outdoor speakers when powered up"
    trigger:
      platform: state
      entity_id: switch.outdoor_speakers
      from: "off"
      to: "on"
    action:
      service: media_player.volume_mute
      data:
        entity_id: media_player.snapcast_client_outdoor_speakers
        is_volume_muted: false

Indoor Setup with Libreelec and Kodi

It wouldn’t be a multi-room audio setup with out multiple clients! So on to the indoor systems. These are the running on my two Libreelec systems, connected to the TVs. The first of these is the most interesting since that connects to to our Polk Signa S2 soundbar. I’m actually planning a review of this in the near future, but for now we’ll just say it sounds awesome. I didn’t include it in the hardware list above since I didn’t purchase it just for this project.

I connected to the soundbar using the second USB soundcard and audio cable. This means I can play audio without having the TV on, just by setting the soundbar to it’s AUX input. The other system in the master bedroom, just sends audio via the HDMI port to the TV.

Multi-Room Audio on Libreelec
Settings of the Libreelec Snapclient Plug-in

On the software side of these I used the excellent Libreelec Snapclient plug-in. Just install it from the official Libreelec repo and you’re good to go. In order for the auto-discovery to work, you should make sure that the Snapserver and Libreelec machine are on the same network. The only other issue I had is that sometimes the ‘list sound cards’ dialog in the plugin settings wouldn’t work. I found it easier to just list the devices on the command line with snapclient -l and put the relevant device number into the addon settings.

Conclusion and Next Steps

Overall, this system is pretty great. It’s served us well for outdoor audio all through the summer and has become our primary way of listening to our music collection. There are a few rough edges, like the issues with album art on Android.

The main other point of complaint is the profusion of volume controls. This could be a separate rant altogether, since everything has it’s own volume control for some reason. For this system I just don’t touch the volume in Mopidy and use the individual channel controls in Snapcast. It looks like there is now a plugin to provide better integration here, but I haven’t tried it yet.

The next steps for this system will be to re-build the server side system as part of my ongoing migration to Docker+VMs. At this point I’d like to add a couple more groups to the Snapserver. One of these will be for audio streaming in over Bluetooth. This will allow for us to stream audio directly from our phones to any of the speakers in the house.

The second group will be for TTS notifications from Home Assistant. I know I can move channels between groups via HASS automations to decide where the audio goes. The main stumbling block on this at the moment is how to get the audio from the HASS server to the media server, which will be separate VMs. If anyone has any ideas here, please let me know!

That’s it for now. Thanks for reading!

If you liked this post and want to see more, please consider subscribing to the mailing list (below) or the RSS feed. You can also follow me on Twitter. If you want to show your appreciation, feel free to buy me a coffee.


Home Assistant Automation for My Mornings

Home Assistant Automation in Depth: Making my mornings a little easier

This post may contain affiliate links. Please see the disclaimer for more information.

I was recently asked by someone at work if I had succeeded in completely automating my house yet. My reply was something along the lines of: “Not yet, but I have got a few awesome automations running”. I then proceeded to give an explanation of my morning Home Assistant automation, which I’m going to document here.

Basically, when I get up in the morning and unplug my phone, the following Home Assistant automation is triggered:

  1. Turn on the lights in the kitchen
  2. Turn on the kettle (which has been set ready to go the night before)
  3. Wait five minutes (giving me time to stumble sleepily into the kitchen and it also happens to be roughly how long the kettle takes to boil)
  4. Turn on the TV
  5. Play a TTS based briefing, which includes the days weather forecast and current indoor temperature
  6. Play a two minute news summary from the BBC
  7. Five minutes after the news finishes, another automation kicks in, turning off the TV

Needless to say, my colleague was suitably impressed by this.

How does the house know I’m getting up?

I mentioned above that one of the first things I do when I get up is unplug my phone from its charger. This is key to triggering this Home Assistant automation. On my phone I have a Tasker profile running which will send a HTTP POST request to Home Assistant when the power state of the phone changes. This is used to update the status of a binary sensor in HASS, which reflects the charging state of my phone. Pretty simple.

Of course this wouldn’t work if I didn’t unplug my phone when getting up, or didn’t charge it overnight. Luckily I do, so this approach works well for me. I did initially play around with using a Baysian sensor to attempt to infer when I was in bed. However, that didn’t work very well for me due to lack of data. Most of the data points I had to use in the inference were things that I wanted to turn on from the automation, like the lights and kettle, which was unhelpful. Having extra sensors here, like motion sensors, would help.

The only downside of this approach (at least for me) is that Tasker is not Open Source and there is no viable FOSS replacement. I’m actively looking for replacements to Tasker for this. I’ll post an update if I ever find something that fits all my requirements.

Home Assistant Automation for My Mornings

The YAML code for this Home Assistant automation can be found in my HASS config, but I’ll go through it here. The first part is pretty standard:

automation:  
  - alias: 'Getting Up'
    trigger:
      platform: state
      entity_id: !secret device1_charge_status
      from: 'on'
      to: 'off'
    condition:
      condition: and
      conditions:
        - condition: state
          entity_id: group.presence
          state: home
        - condition: time
          after: "07:00:00"
          before: "10:00:00"

Here we see the trigger from the charging status of my phone. This is followed by a couple of conditions which restrict when the automation runs. If we’re not home for any reason it doesn’t make sense for the house to start turning things on and talking to itself, so that’s the first condition. The second is there just in case I need to charge my phone at other times of day. This only allows the automation to run between 7am and 10am. If my phone needs recharging (and is then unplugged again) before 10am then I’d probably be more worried about what is happening to my battery than the house starting talking.

The second part of the automation just turns things on and waits for me:

    action:
      - service: script.turn_on
        entity_id: script.downlights_bright
      - service: switch.turn_on
        data:
          entity_id: switch.kettle
      - delay:
          minutes: 5

The first script call there is a simple script to set the colour temperature of the kitchen downlight bulbs depending on the time of day, bluer during the day, redder in the evening. I use this script wherever these lights are turned on so that they are always at the right colour temperature. They also switch over in the evening, even if they are already on, thanks to my sunset automations.

Automating the Kettle

The next call there is simply turning on the kettle. This is connected to a dumb 433Mhz socket (controlled by OpenMQTTGateway). The reason for the dumb socket, rather than something smarter, there is that it’s the only one that I’ve found that is rated for the actual power draw of the kettle. It’s heating water from a 10A supply, so it actually pulls 10A. At 230V this is 2300W. Before anyone asks, I’m not taking account of the power factor here since I’d expect the kettle to be an almost purely resistive load. This socket is rated to 2400W and I’ve measured with a power monitor to make sure it stays below this. Most smart sockets on the market seem to only be rated up to 2200W for some reason.

Of course the kettle has to be set up with water in it and turned on at the base for this to work. The only approach to this is to program myself to do this before I go to bed. With this in mind I have another automation which turns off the kettle and sends me a notification to remind me to set it up.

The final call here is the delay. I played around for a while with the value and 5 minutes seems to be about right. Once I get some more sensors installed (door and/or motion sensors), I’ll tweak the rest of the automation to be triggered on the first time someone enters the kitchen.

Making the house talk

Now we get to something more interesting:

      - service: input_boolean.turn_on
        entity_id: input_boolean.tts_enabled
      - condition: state
        entity_id: media_player.tv
        state: idle
      - service: script.turn_on
        entity_id: script.tts_status_report
      - delay:
          seconds: 33
      - service: tts.picotts_say
        entity_id: media_player.tv
        data_template:
          message: "The latest news from the BBC follows:"
      - delay:
          seconds: 5
      - service: script.turn_on
        entity_id: script.play_news_summary

Most of this references my TTS package, which manages TTS announcements in my home. TTS here is provided by PicoTTS for its offline capability. I don’t want anyone listening in to my highly private weather forecast announcements. I’ve been told the voice is a little robotic, but I’d rather my computer sounds like a computer than a real person. Right now all my TTS announcements are sent to the Kodi instance running on the living room TV for playback.

The first thing to do here is to enable TTS notifications which are turned off overnight via an input boolean. All my TTS notifications go through a script which checks this is enabled before talking. This also handles turning on the TV ready to speak:

script:
  tts_living_room:
    alias: "Living room TTS announcement"
    sequence:
      - condition: state
        entity_id: input_boolean.tts_enabled
        state: 'on'
      - service: switch.turn_on
        entity_id: switch.living_room_tv
      - delay:
          seconds: 3
      - service: tts.picotts_say
        entity_id: media_player.tv
        data_template:
          message: "{{ message }}"

Before we get to the actual morning briefing there is one more check to do. This handles the case that I get a lie in and the rest of the family are up before me (rare!). In this case it’s likely that the TV is already playing something, so we can check if it is idle before continuing.

The Morning TTS Briefing

Now comes the TTS briefing, which is implemented by the following script:

tts_status_report:
    alias: Morning
    sequence:
    - service: script.tts_living_room
      data_template:
        message: >
          {% if now().strftime("%H")|int < 12 %}
          Good morning.
          {% elif now().strftime("%H")|int < 18 %}
          Good afternoon.
          {% else %}
          Good evening.
          {% endif %}
          It's currently {{states.sensor.dark_sky_summary.state}} and {{states.sensor.dark_sky_temperature.state|round}} degrees. The weather for the next 24 hours will be {{states.sensor.dark_sky_hourly_summary.state|replace(".", "")}}, with a high of {{states.sensor.dark_sky_daytime_high_temperature.state|round}} and a low of {{states.sensor.dark_sky_overnight_low_temperature.state|round}}.
          The current average indoor temperature is {{ states.sensor.average_indoor_temperature.state }} degrees.

This gives me a pretty good overview of the day. At some stage I’d like to add notification of upcoming calendar events from Nextcloud via CalDav, but I haven’t got around to it yet. The variable greetings based on time of day add some complexity to the templating. These aren’t necessary for this application, but they enable easy reuse of the script for other purposes. Perhaps a returning home announcement?

The next part of the automation is a bit of a hack. The problem appears to be that HASS will continue executing the automation without waiting for the TTS to finish. For now I wait a fixed delay after each part of the announcement before starting the next. Waiting for the media player to change state back to idle would be better, but I haven’t worked out how to do that yet. I haven’t got the timings quite right since the announcement time varies slightly (depending on the weather!). There is a little bit of delay between each part, but it’s acceptable.

The next part does a simple TTS announcement which introduces the news, bypassing the main TTS script because we just ran it. In this case everything is ready and we don’t want the extra delay that script introduces.

Playing the news

The BBC have a nice two minute news summary, which is available as an MP3 file. This will play fine directly from the web via Kodi and Home Assistant’s media_player.play_media service, if you can get the URL, which is something the BBC don’t make particularly easy. There is no podcast RSS feed for this as there are for many other BBC shows. They also seem to be going out of their way to deliberately obfuscate the URL. My script has broken once already in the <6 months I’ve had it running. Since it’s probably subject to change in the future I won’t embed my URL extraction script here, just follow the link to the latest version. Suffice to say Python+BeautifulSoup to the rescue!

This script is run via a command line sensor in HASS, which updates once per hour. Again, this is to make the resulting script more re-usable – it will always play the latest news:

sensor:
  - platform: command_line
    name: BBC News Summary URL
    scan_interval: 3600
    command: "/home/homeassistant/.homeassistant/shell_commands/extract_news_link.py"

Then there is the corresponding HASS script, which sends the URL to the media player:

script:
  play_news_summary:
     alias: "Play a BBC News Summary"
     sequence:
       - service: switch.turn_on
         entity_id: switch.living_room_tv
       - service: media_player.play_media
         entity_id: media_player.tv
         data_template:
           media_content_id: "{{ states('sensor.bbc_news_summary_url') }}"
           media_content_type: "music"

And that’s pretty much it.

Conclusion

This setup has been working pretty well for the last few months, with a couple of minor hicups caused by external factors. The change to the BBC news obfuscation was one. The other was an issue with Node-RED on the Pi connected to the TV, which caused it not to be able to turn on automatically).

The only other issue I have with this is with regards to the TV volume. The TV remote control does not pass through button presses of the volume keys via HDMI CEC. Therefore, we use the TV volume as the master, with the other volume controls in Kodi/Alsa set to maximum. The problem with this is if it is left on a high volume the night before you’ll get a very loud TTS announcement in the morning! Since the volume level on the TV is not controllable from HASS I can’t adjust the volume to an acceptable level before playing the TTS. The solution to this is to use the Kodi volume control as the master volume, which is controllable from HASS. This requires a new remote for use with Kodi and I’m still looking into options here.

Further Improvements

Aside from the improvements listed above, I’d also like to expand the information given in the TTS briefing. I’ve already touched on adding calendar events. Adding UV exposure information would also be very useful, given the lethal NZ sun.

Hopefully, someone will find some of the approaches and information in this post useful. There are a lot of example configurations available for HASS now. However, it’s often hard to piece the parts together that make everything work for a specific Home Assistant automation without a detailed explanation. This is something I’d like to do more of for my own setup, so hopefully there will be some more posts covering other parts of my configuration in future. Until then, bye.

If you liked this post and want to see more, please consider subscribing to the mailing list (below) or the RSS feed. You can also follow me on Twitter. If you want to show your appreciation, feel free to buy me a coffee.

How my Chromecast breaking was the best thing that ever happened to my TV

As detailed in my recent self-hosting update, I’ve been using a Raspberry Pi running OSMC and Kodi as my frontend for TV recordings and for locally streamed media from Emby, since moving into the new house. We’ve supplemented this with a Chromecast to allow us to access Netflix and a local streaming service Lightbox. This has worked well and integrates with Home Assistant reasonably well, so I can automatically dim the lights, etc. when we are watching TV in the evening.

That was until the Chromecast started behaving oddly.

It started as occasionally corrupted audio when starting a new stream (basically the audio would sound like everyone had been breathing helium). Each time this occurred it was remedied by rebooting the Chromecast, at first by cycling the power, then as the problem persisted via a Python script wired to a button in Home Assistant. This went on for a month or so before things got worse.

The next problem was the Chromecast just flat out refusing to load anything from Lightbox. I spent an evening debugging this to have the thing fall off the network and refuse to come back. It must have automatically recovered itself because the next day it was back and working fine. Then a couple of days later it started to have similar issues again, only now with various HDMI picture issues (not detected or video stained pink).

Clearly it was on it’s way out (suspiciously it was just over a year old, which puts it just out of warranty). Having had enough I unplugged the thing and started to look for other options. Having paid $109 for it to last only a year, I wasn’t happy to buy another Chromecast (I had bought the Chromecast Ultra, but only because it was the only model with built in Ethernet).

Aside: The insidious Chromecast ecosystem

As someone who generally prefers FOSS options wherever possible and has no love of DRM, I’ve always had issues with the Chromecast. That said, as someone who wants attain the media I watch via legal means I appreciate that it allowed me to do that. I also liked the ability to control it from my phone as well as play/pause/stop streams via the TV remote and the aforementioned integration with Home Assistant. Basically, I saw it as a necessary evil.

What I didn’t appreciate is what it does to your phone. Before you have even set the thing up you have to install the proprietary Google Home app (why it can’t have a web interface for configuration I don’t know), then every streaming app that supports it is proprietary (even the Emby one), which left me with a gaggle of proprietary apps on my phone which is mostly otherwise populated with Open Source apps from F-Droid. This severely limited my ability to go GApps free, which has been something I’ve wanted to do for quite some time.

So if I could find another option that didn’t rely on my phone, I could get rid of all these horrible apps (some of which I even have to have Magisk installed in order to persuade them that I don’t have root access).

Meanwhile, back at the plot…

Faced with replacing the Chromecast I had two options. The first was to plug the not so smart TV back in to the network and use the built in apps. This was sub-optimal as it didn’t integrate with Home Assistant, couldn’t be controlled from my phone and the Lightbox app on that TV has broken at least once (in fact I don’t even know if it works now, since I went back to using the Chromecast instead).

The second option was to get Kodi to do it all (I guess there was really a third option which was to go out and find some other streaming device, but I really didn’t want to waste my money again).

Kodi To The Rescue!

To cut a long story short, I managed to get everything working with Kodi over the course of a Sunday afternoon. I already knew there was a Netflix addon (requiring Kodi 18), which I’d been meaning to try, but I didn’t know of a Lightbox addon. A quick search turned up Matt Huisman’s Lightbox Addon, which works great (I’d already used his TVNZ OnDemand and 3NOW addons).

Getting Netflix working turned out to be a bit of a pain, since I had to upgrade to the Kodi 18 Alpha release. I followed these instructions, which didn’t work to start with, but that turned out to be down to a corrupted SD card (weirdly the card was fine in normal use but didn’t like installing new packages). After grabbing a new card and restoring from a backup image I was able to update successfully and install the Netflix addon, which works flawlessly.

The Good

Overall, I’m really happy with this setup. The alpha version of Kodi is surprisingly stable (on par with the release version from my experience so far but YMMV), notwithstanding a couple of bugs which I’ll come to shortly. Netflix and Lightbox work pretty much flawlessly and I’m appreciating the newly unified and simplified media system. I’ve already started implementing further integrations with the home automation, which will be the subject of another post.

The Not So Good

I mentioned above that there are a couple of bugs, but I actually suspect that both issues I’m seeing are down to a common cause. The two issues I’m seeing are both related to TV recordings from TVheadend, with audio sync issues as well as raised RPi temperatures on HD recordings and dropped frames on SD recordings. I still need to get around to updating to the latest nightly release and gathering the debug logs required to submit a proper bug report, but since the TV is a ‘production’ system I haven’t got to this yet. This is the kind of issue, that whilst annoying, isn’t a show stopper and that I would fully expect to be fixed by the final release of Kodi 18.

The only other not so good point is that whilst the Netflix and Lightbox plugins are excellent, navigating through the menus is a little slow. I’m putting this down to the need to fetch the listings over the network every time and probably even scrape the respective websites. I would assume that neither site provides a proper API given how generally hostile streaming services are to third party integrations. Perhaps this could be mitigated either in Kodi or the addons by caching the data for a period of time, since it doesn’t change that often. This definitely isn’t a criticism of either of these addons, I’m impressed that they work as well as they do given their adverse environment. Luckily playback in both is flawless.

Conclusion

Again, I’m really happy with this setup. It’s finally given me an almost 100% Open Source (less the binary blobs required for Widevine DRM) media setup, which doesn’t compromise on functionality and sources all the content via legal means. Kodi gets an undeservedly bad reputation in the media for being a platform which enables piracy, something which the project developers have quite rightly distanced themselves from. Having more addons for legitimate services will help to give the platform a better name (of course if the media industry would wake up and just offer DRM free media at a reasonable cost [i.e. not the same price as a physical copy], that would be even better – but I don’t see that happening any time soon). The most annoying thing about this bad reputation for me is every how to guide for Kodi advertising VPNs (of varying levels of dodgyness) at you, as if the copyright police are going to bang down your door for using Kodi with your own media or a legitimate streaming service.

I’m finding this setup to actually be more feature full than the previous set up. This is obvious when you think about it, since with everything running through Kodi all my media benefits from the huge amount of work that has gone into that project over the years. Whereas, with the siloed approach taken by the individual streaming services they are all doomed to reproduce features that may be in competing services or have been features of established media players since the beginning of time. One really nice feature is that Kodi will make the full metadata of media playing via addons available via it’s various API’s which means that remote control apps can see it, but also that it gets pushed through to Home Assistant. This was hit and miss with the Chromecast (Lightbox would provide some metadata, Netflix would provide none).

Basically, this setup is what the smart TV was meant to be, before the interests that compete with producing the best technical solution got their hands on it.

I mentioned earlier that I’m already planning a follow up post to this one. The upcoming post will detail some of the integration work I’ve been doing to integrate my media setup with Home Assistant. Please stay tuned for that in the coming weeks. Until then, bye.