home assistant wall mounted tablet

Home Assistant Wall Mounted Tablet Update

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

This is an update to my previous post on my wall mounted Home Assistant tablet. There have been quite a few changes, so I thought it was worth another post on the subject. Let’s get into it…

Power Failure

I’ll start with the big issue which has plagued this project – power. Since the battery in the original tablet was completely exhausted I had built a wired power supply to replace it. Unfortunately, this wasn’t powerful enough and the tablet would power off sometimes under high load. This problem seemed to get worse until the tablet would power off after just a few minutes, thus becoming pretty much useless.

home assistant wall mounted tablet
The new PSU all wired up (difficult to capture in a photo!)

I mentioned previously that I had ordered a 5A power supply to fix this problem, but even that didn’t work. The tablet must pull transient currents of more than 5A, which a battery can supply quite easily. Unfortunately the fixed power supply cannot. This is a real shame because I put a lot of effort into the new power supply – I even printed a case for it based on a modified version of the parametric PCB case I used in my recent MySensors build.

Even with the failure of the new power supply, the exercise in building this has demonstrated the utility of a wall mounted tablet in our household. The decision was therefore taken to replace the old broken tablet with a new one.

The New Tablet

The new tablet is the Lenovo Tab E8 (link is to Amazon, but ours came from a local retailer), chosen solely because it was the cheapest 8 inch tablet available!

home assistant wall mounted tablet
The new tablet mounted on the wall, complete with new brackets

The tablet seems pretty good overall, at least for the price. The build quality is fine, although the case is very plastic-y. It’s a little slow after boot up as it loads up everything, but in normal usage as a wall mounted dashboard it performs perfectly well. The shipped operating system is Android 7.0, which is up to date enough to not present any problems with app support.

The biggest issue with this device is that it charges really slowly, even from the provided charger. I even had cases when setting it up that the battery would still drain when plugged in to the charger. I was a little concerned that the tablet battery would become discharged over time during normal use, due to wake ups from the motion sensing. However, once the tablet was mounted on the wall and left overnight it charged to 100% and has remained there since.

Fixing the Brackets

I printed new brackets for the tablet with a reduced front lip. This removes the issue with the brackets obscuring the screen. The right bracket (left from the point of view of the tablet and in the STL files) also has a cut out in it’s side to allow the USB cable to pass through. I made these edits in OpenSCAD, by first importing the original STL files. I then created an object which represents the material I wanted to remove. Then a difference operation between the two gives the final object. I’m pretty pleased with them. The only thing I wasn’t able to reproduce was the rounded internal edges on the cut out sections.

Wall Panel MQTT Integration

With the power issues solved, I can integrate the tablet fully with Home Assistant (without fear that the extra load will cause it to power off). I’ve integrated in the sensors available on this device (pretty much just the power status). The full YAML code for this is available on GitLab. This pretty much went according to the WallPanel documentation. However, I did find that I needed to specify a value_template in order to extract the battery value:

sensor:
  - platform: mqtt
    state_topic: "wallpanel/kitchen/sensor/battery"
    name: "Kitchen WallPanel Battery Level"
    unit_of_measurement: "%"
    value_template: '{{value_json.value}}'
    device_class: battery

To extract the boolean status for the charging state sensors, the template gets a little more complex:

binary_sensor:
  - platform: mqtt
    state_topic: "wallpanel/kitchen/sensor/battery"
    name: "Kitchen WallPanel Charge State"
    value_template: '{% if value_json.charging %}ON{% else %}OFF{% endif %}'
    device_class: power

  - platform: mqtt
    state_topic: "wallpanel/kitchen/sensor/battery"
    name: "Kitchen WallPanel AC State"
    value_template: '{% if value_json.acPlugged %}ON{% else %}OFF{% endif %}'
    device_class: plug

I also integrated in the motion, face detection and QR code sensors, although I’m not using the face detection or QR code sensors and have since disabled them in WallPanel.

sensor:
  - platform: mqtt
    state_topic: "wallpanel/kitchen/sensor/qrcode"
    name: "Kitchen QR Code"
    value_template: '{{value_json.value}}'

binary_sensor:
  - platform: mqtt
    state_topic: "wallpanel/kitchen/sensor/motion"
    name: "Kitchen Motion"
    payload_on: '{"value":true}'
    payload_off: '{"value":false}'
    device_class: motion

  - platform: mqtt
    state_topic: "wallpanel/kitchen/sensor/face"
    name: "Kitchen Face Detected"
    payload_on: '{"value":true}'
    payload_off: '{"value":false}'
    device_class: motion

The final integration I did was to add a switch to turn on and off the camera streaming. However, in general usage this seems a bit flakey, with the camera stream not usually becoming available until after a WallPanel restart. Here’s the YAML used for the switch anyway:

switch:
  - platform: mqtt
    command_topic: "wallpanel/kitchen/command"
    name: "Kitchen Camera State"
    payload_on: '{"camera": true}'
    payload_off: '{"camera": false}'
    optimistic: true
    retain: true

Dashboard Improvements

home assistant wall mounted tablet
My Current Dashboard

I’ve improved my dashboard somewhat since my last post. However, it’s still a long way from some of the awesome dashboards I’ve seen posted. It’s still a work in progress!

The main changes have been to switch to the animated weather custom card for the weather forecast and to add Custom Header to my install to compact the header. I then added more buttons, some media controls and a gauge showing the progress of my 3D prints. The vacuum buttons call some custom scripts to start the vacuum with our preferred settings. This is necessary to work around a bug in HASS with the Botvac D3:

script:
  spot_clean:
    alias: "Spot Clean"
    sequence:
      - alias: "Start spot clean"
        service: vacuum.neato_custom_cleaning
        data:
          entity_id: vacuum.marvin
          mode: 2
          category: 3

  house_clean:
    alias: "House Clean"
    sequence:
      - alias: "Start house clean"
        service: vacuum.neato_custom_cleaning
        data:
          entity_id: vacuum.marvin
          mode: 1
          category: 2

It should be noted that I’m running a slightly older version of HASS at the moment. In newer versions the service name changed to neato.custom_cleaning. Also, using a category value of 3 for spot cleaning appears to be undocumented, but if you look at the library, that’s what it’s doing.

Conclusions

This setup is now working perfectly and all the problems with the previous iteration have been resolved. It’s unfortunate that this was done by throwing money at the problems, but sometimes things just don’t work out as we hope!

Overall, I’m really happy with the finished product. The dashboard will be continually developing as I come up with ideas and work out what’s useful and what isn’t. Feel free to share your ideas again via the feedback channels.

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.

Zigbee sniffer dongle for zigbee2mqtt

zigbee2mqtt: Cheap Zigbee Without a Gateway

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

I’m continuing to work through my backlog of completed but as yet undocumented Home Automation projects. As such you may be familiar with zigbee2mqtt already, since it’s been around for a while. However, I wanted to document my setup and show off a few automations for some specific use cases of mine.

Introduction

Two sensor projects have constantly eluded me in my ongoing efforts to transform my house into a DIY smarthome. The first of these are open/closed sensors for both internal and external doors and all the windows. The second are some input buttons allowing manual switching of devices in the cases where things aren’t automatically set correctly.

I’ve followed the various options over time and never quite managed to find anything I was happy with. I’ve even strongly considered building my own solution (as I’m doing with my room sensors). However, the trifecta of low power battery operation, nice compact physical design and being cheap enough to install everywhere stumped me. Then the Xiaomi Aqara/Mijia range of sensors started appearing. Those looked perfect, except for that horrible gateway. Enter zigbee2mqtt!

Zigbee2mqtt is a nodejs based software project that uses a cheap zigbee sniffer module with some custom firmware to replace the manufacturers gateways for a range of zigbee devices. As well as the Xiaomi devices it also supports Philips Hue and IKEA Tradfri devices among others. The software connects to the USB serial port provided by the dongle and sends messages via MQTT. It even comes with built in Home Assistant MQTT discovery support! (regular readers will know how much of a fan I am of that).

A Setup for Testing

To start playing around with these devices and not really knowing how well they were going to work in general and specifically with zigbee2mqtt, I decided to start with just a few devices. To this end, I ordered:

In addition, I needed the a CC-debugger programming tool and the associated cable in order to program the CC2531 dongle.

Installation and Setup

Once these arrived I was able to easily program the custom firmware to the CC2531 using the official instructions (I used the Linux version). After that I installed zigbee2mqtt again following the instructions. The only wrinkle in this process was getting the USB serial device to show up in the LXD container that runs my home automation components. This is reasonably specific to my environment of running in LXD, you wouldn’t encounter it on a bare Linux system.

Zigbee sniffer dongle for zigbee2mqtt
The zigbee sniffer dongle installed in the server. The green LED is extremely bright!

After that I set about pairing my devices, again following the excellent documentation. I found that I had to have my devices very close (within 30cm) to the dongle during the pairing process. I’m not familiar with the specifics of the Zigbee pairing process. However, I’m assuming that it uses a very low transmit power. This would act as a security measure to prevent pairing with unauthorised devices. Once the devices were paired I was immediately able to see the door sensors as binary sensors in HASS.

I was able to complete this whole process in about an hour. It was really almost too easy. Huzzah for more than adequate documentation and things going according to plan (that almost never happens).

Reading Button Presses

Integrating the buttons into Home Assistant is a little more difficult. This is because there is no built in abstraction for a button or control surface. Instead we fall back to catching the value published via MQTT when the button is pressed (actually, I note that there is a way to do this using an entity now, but that didn’t exist at the time I did it). Here is the set of automations for the button I put in my kitchen:

automation:
  - alias: Kitchen button downlights on
    trigger:
      platform: mqtt
      topic: 'zigbee2mqtt/kitchen_button'
    condition:
      condition: and
      conditions:
        - condition: template
          value_template: "{{ 'single' == trigger.payload_json.click }}"
        - condition: template
          value_template: "{{ states.light.kitchen_downlights.state == 'off' }}"
    action:
      service: script.turn_on
      data:
        entity_id: script.downlights_bright

  - alias: Kitchen button downlights off
    trigger:
      platform: mqtt
      topic: 'zigbee2mqtt/kitchen_button'
    condition:
      condition: and
      conditions:
        - condition: template
          value_template: "{{ 'single' == trigger.payload_json.click }}"
        - condition: template
          value_template: "{{ states.light.kitchen_downlights.state == 'on' }}"
    action:
      service: light.turn_off
      data:
        entity_id: light.kitchen_downlights

  - alias: Kitchen button dining room on
    trigger:
      platform: mqtt
      topic: 'zigbee2mqtt/kitchen_button'
    condition:
      condition: and
      conditions:
        - condition: template
          value_template: "{{ 'double' == trigger.payload_json.click }}"
        - condition: template
          value_template: "{{ states.light.dining_room_spots.state == 'off' }}"
    action:
      service: scene.turn_on
      entity_id: scene.dining_spots_bright

  - alias: Kitchen button dining room off
    trigger:
      platform: mqtt
      topic: 'zigbee2mqtt/kitchen_button'
    condition:
      condition: and
      conditions:
        - condition: template
          value_template: "{{ 'double' == trigger.payload_json.click }}"
        - condition: template
          value_template: "{{ states.light.dining_room_spots.state == 'on' }}"
    action:
      service: light.turn_off
      data:
        entity_id: light.dining_room_spots

  - alias: Kitchen button all lights on
    trigger:
      platform: mqtt
      topic: 'zigbee2mqtt/kitchen_button'
    condition:
      condition: and
      conditions:
        - condition: template
          value_template: "{{ 'long' == trigger.payload_json.click }}"
        - condition: or
          conditions:
            - condition: template
              value_template: "{{ states.light.kitchen_downlights.state == 'off' }}"
            - condition: template
              value_template: "{{ states.light.dining_room_spots.state == 'off' }}"
            - condition: template
              value_template: "{{ states.light.living_room_spots.state == 'off' }}"
    action:
      service: scene.turn_on
      entity_id: scene.main_lights

  - alias: Kitchen button all lights off
    trigger:
      platform: mqtt
      topic: 'zigbee2mqtt/kitchen_button'
    condition:
      condition: and
      conditions:
        - condition: template
          value_template: "{{ 'long' == trigger.payload_json.click }}"
        - condition: template
          value_template: "{{ states.light.kitchen_downlights.state == 'on' }}"
        - condition: template
          value_template: "{{ states.light.dining_room_spots.state == 'on' }}"
        - condition: template
          value_template: "{{ states.light.living_room_spots.state == 'on' }}"
    action:
      - service: light.turn_off
        entity_id: light.kitchen_downlights
      - service: light.turn_off
        entity_id: light.dining_room_spots
      - service: light.turn_off
        entity_id: light.living_room_spots

  - alias: Kitchen button candlelight dinner
    trigger:
      platform: mqtt
      topic: 'zigbee2mqtt/kitchen_button'
    condition:
      condition: and
      conditions:
        - condition: template
          value_template: "{{ 'triple' == trigger.payload_json.click }}"
    action:
      - service: light.turn_on
        data:
          entity_id: light.dining_room_spots
          brightness_pct: 1
      - service: light.turn_off
        entity_id: light.kitchen_downlights
      - service: light.turn_off
        entity_id: light.living_room_spots

Phew, that was ALOT of YAML! Each of these automations is basically the same, with a trigger on the relevant topic from MQTT. There then follows some conditions. The most important of these uses a template to unpack the click field of the JSON payload and check it against the type we are interested in. In this way we can do different things depending on the click type (e.g. single, double and triple clicks or long presses).

The other conditions in the automations perform some state checking to make sure that the action performed is correct depending on the current state of the house. For example there is no point turning the kitchen downlights on when they are already on. Instead the user clearly wants to turn them off. I spent quite some time tweaking these conditions over a few days of usage to get them just right. The final (and easiest) part of each automation is just performing the required action (or actions).

Button Functionality

With all this in place we end up with a single button which has the following functionality:

  • On a single click turn on or off the kitchen downlights, depending on their state at the time
  • For a double click turn on or off the dining room spotlights, again depending on their state
  • On a long click turn on or off all lights in our main living space, again depending on their state
  • A triple press turns off all the lights except the dining room which get dimmed (our romantic dinner scene)

That doesn’t even exhaust the capabilities of this switch. It can do quadruple (and maybe quintuple?) presses as well as variable length long presses. At some point the usability just starts to get silly though.

Xiaomi button driven by zigbee2mqtt
I installed one of the buttons in the kitchen, above the other light switches.

I have the other button set up to do something similar. However it’s on;y switching two things so it’s a little less interesting than the example above.

Monitoring Battery Status

Since these are battery powered devices, it’s useful to be able to monitor the battery level. In doing so we can avoid the otherwise inevitable situation of a significant other pressing the button and the lights not coming on. Luckily, zigbee2mqtt has us covered in this respect. For my buttons and sensors I added the following sensors for my battery levels:

sensor:
  - platform: mqtt
    name: "Kitchen/Hall Door Battery"
    state_topic: "zigbee2mqtt/kitchen_hall_door"
    unit_of_measurement: '%'
    value_template: "{{ value_json.battery }}"
  - platform: template
    sensors:
      kitchen_button_battery:
        friendly_name: "Kitchen Button Battery"
        unit_of_measurement: "%"
        value_template: "{{ states.sensor.kitchen_button.attributes.battery }}"

Here I’m just showing two sensors, one for the battery level of the door sensor and one for the battery level of the button. I obviously have similar configuration entries for the other devices.

For some reason the configuration required is a little inconsistent here. I found this was because the button has an entity created via MQTT discovery, which includes the battery level as an attribute, whilst the battery level is not included in the attributes for the door sensor entity.

This means that we create a raw MQTT sensor for the door sensor battery level and extract the reading out in the value_template. For the button battery sensor, we use a template sensor and just pull the required attribute out.

Battery Notifications

Of course, just having the battery levels as entities in Home Assistant is of limited use. What we really want it a notification when the battery gets low:

automation:
  - alias: Low Battery Notification
    trigger:
      - platform: numeric_state
        entity_id: sensor.kitchenhall_door_battery
        below: 20
      - platform: numeric_state
        entity_id: sensor.kitchen_button_battery
        below: 20
    action:
      service: notify.notify
      data_template:
        title: "Low Battery Alert"
        message: "Device '{{ trigger.to_state.name }}' has LOW battery: {{ trigger.to_state.state }}%."

Here we are triggering when any of the battery sensors we are interested in get below 20%. We then send the notification via the default notification service, using the trigger template variable to include the name of the sensor that triggered the alert and the state.

I actually have two such rules in my set up. This one at 20% and another critical warning at 5%. In this way I get an early heads up of a low battery in order to check I have replacement batteries ready. I then get another when the battery actually needs changing.

Quick Bonus Automation

As I haven’t said much about the actual door sensors in this post, here are the automations I use to turn on and off a dimmed downlight in the kitchen if the door opens at night:

automation:
  - alias: "Night mode kitchen light on"
    trigger:
      - platform: state
        entity_id: binary_sensor.kitchen_hall_door
        from: "off"
        to: "on"
    condition:
      - condition: state
        entity_id: input_select.home_mode
        state: "Night"
    action:
      - alias: Lights to Goodnight Scene
        service: scene.turn_on
        data:
          entity_id: scene.goodnight

  - alias: "Night mode kitchen light off"
    trigger:
      - platform: state
        entity_id: binary_sensor.kitchen_hall_door
        from: "on"
        to: "off"
        for:
          seconds: 30
    condition:
      - condition: state
        entity_id: input_select.home_mode
        state: "Night"
    action:
      - alias: Light Off
        service: light.turn_off
        entity_id: light.kitchen_downlight_1

These are pretty self explanatory so I won’t go into them further. The interesting thing here is that the combined reaction time of the sensors, HASS server and smart bulb is so quick that the light is always on by the time the door is open enough to see the bulb. More than once this has tricked me into thinking that the off automation is not actually working and that the light is staying on all night. I found myself waiting outside the door a few times for the light to go off, until I had built up confidence that it was working as expected.

Xiaomi door sensor driven by zigbee2mqtt
One of the door sensors installed on the door. Due to the design of our doors I opted to install the sensor on the inside of the door jamb with the magnet at 90 degrees, which works perfectly.

Impressions of the System

I’m really happy with both the software and hardware components of this project. Aside from the inevitable tweaking of the automation rules I had all four devices that I ordered working in an evening. This included flashing the dongle, installing zigbee2mqtt and all the HASS configuration and automations. Zigbee2mqtt seems to be a pretty well put together piece of software with excellent documentation. I’ve also had no stability issues from it in the several months it’s been running.

In terms of the sensors themselves, they are physically quite nice and well designed. Although they are made of plastic, the matte finish on the outside makes them feel more expensive than they are. They are also paintable, if you are so inclined. The adhesive tape used to stick the devices to the wall is also incredibly strong. You really have to make sure you get them in the right place before you push down!

Conclusion

Overall, I can recommend both zigbee2mqtt and these specific devices for anyone wanting to introduce zigbee devices to their home automation system. The barrier to entry both in terms of cost and set up time/complexity is low if you already have a bit of Linux and Home Assistant knowledge. For example, if you’ve been running a HASS server for a little while and have tacked a few other integration projects you should have no problem. At this stage I wouldn’t recommend it for absolute newcomers due to the firmware flashing part.

For my part, I will definitely be buying more of the window/door sensors with the aim of fitting out the whole house eventually. I’ll probably also get a couple more buttons too! In addition I’ll be investigating the ever growing list of zigbee2mqtt supported devices to see what else I can add to my system over time.

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.

HDMI CEC Flow

HDMI CEC for Home Assistant with Node-RED

I set out on a Sunday morning thinking this was going to be a quick project and, not having decided on a blog topic for this week, it seemed like the ideal candidate. I was wrong – about it being a quick project, hopefully not about it being a reasonable subject for a blog post.

This post is brought to you by issue #12846 in Home Assistant (and the letter ‘C’). That is to say, one of my automations was broken by this issue, which has been sitting open on GitHub since the beginning of march with no progress. I don’t want this to sound like the usual “user of Open Source application complains about free stuff”, because I’m not actually complaining. I understand that software breaks and sometimes there aren’t the resources available to fix it. The solution to this is to get more developers paid to work on Free and Open Source Software (but that’s entirely a discussion for outside of this post).

Actually, this post is here to offer a solution (or at least a temporary one) to the issue, outside of Home Assistant, since I couldn’t fix it myself (I took a look at the code in question and I couldn’t work it out – it needs to be done by someone with more familiarity with the Home Assistant core).

My solution is to use Node-RED along with the HDMI CEC nodes to create an auto-discovered MQTT switch with which I can turn on and off my TV. So, let’s get into the flow…

The Flow

HDMI CEC Flow

The HDMI CEC Switch Flow

This flow runs on an instance of Node-RED running on my OSMC based Raspberry Pi sitting behind my TV (for those keeping up at home, this makes two NR instances on my network – so far). Currently, this is the only flow running on this instance, but I’m considering what else I can run now that I have Node-RED available there. I installed Node-RED on OSMC using the official install/upgrade script. I had fully expected installing Node-RED under OSMC to be a major pain, but it turned out to just amount to running that command.

After the install had finished, I created a user for Node-RED since I like it to run under it’s own user and updated the systemd unit file accordingly. I then installed the CEC nodes linked above from the palette manager. Here I ran into a minor bump in the road in that the CEC nodes couldn’t execute the cec-client program. As it turned out the location of that binary is in a weird location on OSMC, so I added the following in the systemd file to set this up:

Environment="PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games:/sbin:/usr/sbin:/usr/osmc/bin:/opt/vc/bin"

I also needed to add my new Node-RED user to the video group to allow access to the CEC device:

sudo gpasswd -a node-red video

Where I really got stuck was playing around with the example flow for the CEC nodes. It wasn’t that it didn’t work as advertised, it was that it broke the CEC command passthrough to Kodi running on the same machine, rendering my TV remote useless within Kodi. Many hours, much futile searching and playing with cec-client later, I still wasn’t any closer to a solution. I knew it must work, because somehow the pycec script I was using previously is able to send an receive CEC packets without interfering with Kodi.

The breakthrough was dropping both a CEC-In and a CEC-Out node into my flow and only grabbing a few CEC packet types in the filter of the input node. I say ‘breakthrough’ – this works most of the time, but it throws a few errors and warnings on start up. I found it to be most reliable when I immediately restarted Kodi after deploying it – this also helps Kodi to regain its CEC connection if necessary.

So How Does It Work?

Oh, yeah. I was going to talk about the flow, but I kinda got sidetracked there.

Well, it’s pretty simple there are two sequences in the flow. One which handles the switch state and MQTT discovery configuration (bottom) and one which handles the incoming commands over MQTT and sending the corresponding CEC commands.

Let’s start with the bottom one:

This sequence has two input paths, the bottom of these executes on start up (or at deploy time) and sends the Home Assistant MQTT Discovery configuration, using the same technique I used in my volcano sensors. The start up message also passes through a 3 second delay before passing to an exec node, which restarts Kodi. I added the following to my sudoers file (via visudo), to allow this:

# Allow node-red to restart kodi
node-red ALL=(ALL) NOPASSWD: /bin/systemctl restart mediacenter

The top input path receives incoming CEC messages of the type REPORT_POWER_STATUS. In my setup, this only receives power messages from the TV, but you may receive messages from other devices on the bus, in this case you can add a check on the source address of the packet in the following function node (clue: the TV is usually address 0).

The message passes through a function node, which converts the power status to the switch status expected by HASS and also sets the MQTT topic:

msg.topic = "homeassistant/switch/tv_living_room/state";
if(msg.payload.data.str == "ON")
{
    msg.payload = "ON";
}
else if(msg.payload.data.str == "STANDBY")
{
    msg.payload = "OFF";
}
return msg;

Both input paths are connected to a common MQTT output node to send their respective messages (config and state) out to Home Assistant.

The top sequence simply subscribes to the command topic from HASS and determines whether the command was on or off. The JSON payload for the CEC command is then set respectively in either branch – this JSON is taken directly from the example flow linked above. Then we pass this out to the CEC adapter – done. When the device acts upon the CEC command it should send its new power state back through and update the state of our switch. The state will also be updated if you turn on the TV by other means, e.g. the remote.

Pure JSON

This JSON was made in clean, green New Zealand from 100% natural ingredients (electrons):

[{"id":"8c6731b6.47fe6","type":"cec-out","z":"faa74966.a2471","cec_adapter":"7b6bdd74.05a08c","name":"Send CEC Command","x":620,"y":80,"wires":[]},{"id":"6851e3fd.f0320c","type":"cec-in","z":"faa74966.a2471","cec_adapter":"7b6bdd74.05a08c","name":"TV Power Status","flow_in":true,"flow_out":false,"select_all":"false","active_source":false,"image_view_on":false,"text_view_on":false,"inactive_source":false,"request_active_source":false,"routing_change":false,"routing_information":false,"set_stream_path":false,"standby":false,"record_off":false,"record_on":false,"record_status":false,"record_tv_screen":false,"clear_analogue_timer":false,"clear_digital_timer":false,"clear_external_timer":false,"set_analogue_timer":false,"set_digital_timer":false,"set_external_timer":false,"set_timer_program_title":false,"timer_cleared_status":false,"timer_status":false,"cec_version":false,"get_cec_version":false,"give_physical_address":false,"get_menu_language":false,"report_physical_address":false,"set_menu_language":false,"deck_control":false,"deck_status":false,"give_deck_status":false,"play":false,"give_tuner_device_status":false,"select_analogue_service":false,"select_digital_service":false,"tuner_device_status":false,"tuner_step_decrement":false,"tuner_step_increment":false,"device_vendor_id":false,"give_device_vendor_id":false,"vendor_command":false,"vendor_command_with_id":false,"vendor_remote_button_down":false,"vendor_remote_button_up":false,"set_osd_string":false,"give_osd_name":false,"set_osd_name":false,"menu_request":false,"menu_status":false,"user_control_pressed":false,"user_control_release":false,"give_device_power_status":false,"report_power_status":true,"feature_abort":false,"abort":false,"give_audio_status":false,"give_system_audio_mode_status":false,"report_audio_status":false,"set_system_audio_mode":false,"system_audio_mode_request":false,"system_audio_mode_status":false,"set_audio_rate":false,"start_arc":false,"report_arc_started":false,"report_arc_ended":false,"request_arc_start":false,"request_arc_end":false,"end_arc":false,"cdc":false,"none":false,"x":120,"y":140,"wires":[["cb58724f.7560e"]]},{"id":"cb58724f.7560e","type":"function","z":"faa74966.a2471","name":"Extract Status","func":"msg.topic = \"homeassistant/switch/tv_living_room/state\";\nif(msg.payload.data.str == \"ON\")\n{\n    msg.payload = \"ON\";\n}\nelse if(msg.payload.data.str == \"STANDBY\")\n{\n    msg.payload = \"OFF\";\n}\nreturn msg;","outputs":1,"noerr":0,"x":300,"y":140,"wires":[["fb6e90b5.114e2"]]},{"id":"fb6e90b5.114e2","type":"mqtt out","z":"faa74966.a2471","name":"Send Messages","topic":"","qos":"2","retain":"true","broker":"e320da15.60a5c8","x":520,"y":180,"wires":[]},{"id":"1e1472f2.aa2665","type":"function","z":"faa74966.a2471","name":"Format config messages","func":"var config = {\n    payload: {\n        name: \"Living Room TV\",\n        command_topic: \"homeassistant/switch/tv_living_room/cmd\",\n    },\n    topic: \"homeassistant/switch/tv_living_room/config\"\n};\nreturn config;","outputs":1,"noerr":0,"x":310,"y":200,"wires":[["fb6e90b5.114e2"]]},{"id":"3fba0578.68931a","type":"inject","z":"faa74966.a2471","name":"@Startup","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":true,"onceDelay":0.1,"x":100,"y":200,"wires":[["1e1472f2.aa2665","e1ab4a22.f0286"]]},{"id":"bde8cf94.665618","type":"mqtt in","z":"faa74966.a2471","name":"TV Command","topic":"homeassistant/switch/tv_living_room/cmd","qos":"2","broker":"e320da15.60a5c8","x":110,"y":80,"wires":[["60969010.3d95e"]]},{"id":"60969010.3d95e","type":"switch","z":"faa74966.a2471","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":270,"y":80,"wires":[["437a5107.5312"],["533b352b.b8d2fc"]]},{"id":"437a5107.5312","type":"change","z":"faa74966.a2471","name":"Turn TV On","rules":[{"t":"set","p":"payload","pt":"msg","to":"{\"source\":null,\"target\":0,\"opcode\":\"IMAGE_VIEW_ON\"}","tot":"json"}],"action":"","property":"","from":"","to":"","reg":false,"x":430,"y":60,"wires":[["8c6731b6.47fe6"]]},{"id":"533b352b.b8d2fc","type":"change","z":"faa74966.a2471","name":"Turn TV Off","rules":[{"t":"set","p":"payload","pt":"msg","to":"{\"source\":null,\"target\":\"0.0.0.0\",\"opcode\":\"STANDBY\"}","tot":"json"}],"action":"","property":"","from":"","to":"","reg":false,"x":430,"y":100,"wires":[["8c6731b6.47fe6"]]},{"id":"e1ab4a22.f0286","type":"delay","z":"faa74966.a2471","name":"","pauseType":"delay","timeout":"3","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":260,"y":260,"wires":[["3fb711f5.af2e76"]]},{"id":"3fb711f5.af2e76","type":"exec","z":"faa74966.a2471","command":"/usr/bin/sudo /bin/systemctl restart mediacenter","addpay":false,"append":"","useSpawn":"false","timer":"","oldrc":false,"name":"Restart Kodi","x":430,"y":260,"wires":[[],[],[]]},{"id":"7b6bdd74.05a08c","type":"cec-config","z":"","OSDname":"Kodi","comport":"RPI","hdmiport":"1","player":false,"recorder":true,"tuner":false,"audio":false},{"id":"e320da15.60a5c8","type":"mqtt-broker","z":"","name":"Home Broker","broker":"mqtt.webworxshop.com","port":"8883","tls":"3f2e52ab.1b319e","clientid":"","usetls":true,"compatmode":true,"keepalive":"60","cleansession":true,"birthTopic":"","birthQos":"0","birthPayload":"","closeTopic":"","closePayload":"","willTopic":"","willQos":"0","willPayload":""},{"id":"3f2e52ab.1b319e","type":"tls-config","z":"","name":"Home Broker","cert":"","key":"","ca":"","certname":"","keyname":"","caname":"","verifyservercert":true}]

Bonus: Home Assistant Automation Rules

Here are the Home Assistant automations that I’m using with this. Basically I’m turning off the TV five minutes after either Kodi or the Chromecast stops playing, unless it started again in the meantime:

- alias: 'Start shut off timer when TV becomes idle'
  trigger:
    - platform: state
      entity_id: media_player.tv
      to: idle
    - platform: state
      entity_id: media_player.living_room
      to: idle
    - platform: state
      entity_id: media_player.living_room
      to: 'off'
  action:
    service: timer.start
    entity_id: timer.tv_off

- alias: 'Cancel shut off timer if TV starts playing'
  trigger:
    - platform: state
      entity_id: media_player.tv
      to: playing
    - platform: state
      entity_id: media_player.living_room
      to: playing
  action:
    service: timer.cancel
    entity_id: timer.tv_off

- alias: 'When timer expires, turn off TV'
  trigger:
    - platform: event
      event_type: timer.finished
      event_data:
        entity_id: timer.tv_off
  action:
    service: switch.turn_off
    data:
      entity_id: switch.living_room_tv

This uses a timer, which is defined as:

tv_off:
  duration: "00:05:00"

Done. Now we can be lazy/forgetful about leaving TV on and also not waste power. Mission Accomplished.

Conclusion

Hopefully, someone will find time to fix the bug above. I’m probably going to stick with this regardless because I had some other issues running pyCEC on top of OSMC – mainly because they don’t build the libcec bindings for Python 3 by default. I had some custom patches to do this, but it would break (in one way or another on every update). Hopefully, this solution should be more robust. Also, the MQTT connection used in this solution runs over TLS (rather than the unencrypted TCP of the pyCEC network mode), so there is a little security win. Plus, as I already mentioned, now I have a Node-RED instance on a Pi in my living room.

Tiny MQTT Broker with OpenWRT

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

So yet again I’ve been really lax at posting, but meh. I’ve still been working on various projects aimed at home automation – this post is a taster of where I’m going…

MQTT (for those that haven’t heard about it) is a real time, lightweight, publish/subscribe protocol for telemetry based applications (i.e. sensors). It’s been described as “RSS for the Internet of Things” (a rather poor description in my opinion).

The central part of MQTT is the broker: clients connect to brokers in order to publish data and receive data in feeds to which they are subscribed. Multiple brokers can be fused together in a heirarchical structure, much like the mounting of filesystems in a unix-like system.

I’ve been considering using MQTT for the communication medium in my planned home automation/sensor network projects. I wanted to set up a heirarchical system with different brokers for different areas of the house, but hadn’t settled on a hardware platform. Until now…

…enter the TP-Link MR3020 ‘travel router’, which is much like the TL-WR703N which I’ve seen used in several hardware hacks recently:

It's a Tiny MQTT Broker!

It’s a Tiny MQTT Broker!

I had to ask a friend in Hong Kong to send me a couple of these (they aren’t available in NZ) – thanks Tony! (UPDATE 2019: Of course now you can get these shipped direct, something I didn’t know about in 2012). Once I received them installing OpenWRT was easy (basically just upload through the exisiting web UI, follow the instructions on the wiki page I linked to above). I then configured the wireless adapter in station mode so that it would connect to my existing wireless network and added a cheap 8GB flash drive to expand the available storage (the device only has 4MB of on-board flash, of which ~900KB is available after installing OpenWRT). I followed the OpenWRT USB storage howto for this and to my relief found that the on-board flash had enough space for the required drivers (phew!).

Once the hardware type stuff was sorted with the USB partitioned (1GB swap, 7GB /opt) and mounting on boot, I was able to install Mosquitto, the Open Source MQTT broker with the following command:

$ opkg install mosquitto -d opt

The -d option allows the package manager to install to a different destination, in this case /opt. Destinations are configured in /etc/opkg.conf.

It took a little bit of fiddling to get mosquitto to start at boot, mainly because of the custom install location. In the end I just edited the paths in /opt/etc/init.d/mosquitto to point to the correct locations (I changed the APP and CONF settings). I then symlinked the script to /etc/rc.d/S50mosquitto to start it at boot.

That’s about as far as I’ve got, apart from doing a quick test with mosquitto_pub/mosquitto_sub to test everything works. I haven’t tried mounting the broker under the master broker running on my home server yet.

The next job is to investigate the serial port on the device in order to attach an Arduino clone which I soldered up a while ago. That will be a story for another day, hopefully in the not-to-distant future!

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.