The benefit of open source

I upgraded Home Assistant to v0.62 at the weekend and my 433MHz switches stopped working. Odd though it might sound, that’s proved to me once again the benefit of open source software.

The first thing to do was check the logs to see if there were any errors. Sure enough there were:

2018-01-27 16:35:28 ERROR (Thread-17) [homeassistant.components.rfxtrx] Invalid device: 7100a0041010000 2018-01-27 16:35:28 ERROR (Thread-17) [homeassistant.components.rfxtrx] Invalid device: 7100a0041020000 2018-01-27 16:35:28 ERROR (Thread-17) [homeassistant.components.rfxtrx] Invalid device: 7100a0041030000

So they’re my switches and for some reason they’re now triggering an error.

I have other 433MHz devices – a doorbell and a weather sensor – and I checked to see if they were working. They were, so the issue was specific to the switches and not an overall problem with RFXtrx.

If I was running commercial software that’s as far as I could’ve taken my investigation. The next step would’ve been to contact support, maybe raise a ticket and post on some forums.

Home Assistant is open source, though, so I had other options – I had the source code. Being Python as well I had an easy way to install an older version. I’d actually skipped 0.61, so the last working version I had was 0.60.

I installed 0.61 and tested that:

pip install homeassistant==0.61

The issue was still present. I tested 0.60 again to confirm that it worked in order to rule out an external factor as the cause. It was fine, so I knew the issue was introduced in 0.61.

I had the error message so I decided to track down where it was being generated. I did a search from the lib/python3.5/site-packages/homeassistant directory of my Home Assistant virtual environment:

grep -r 'Invalid device:' *
components/rfxtrx.py:            _LOGGER.error("Invalid device: %s", packet_id)

That was a result. Only one match, and it’s in a very likely looking file. I found the relevant lines in rfxtrx.py:

def get_devices_from_config(config, device):
    """Read rfxtrx configuration."""
    signal_repetitions = config[CONF_SIGNAL_REPETITIONS]

    devices = []
    for packet_id, entity_info in config[CONF_DEVICES].items():
        event = get_rfx_object(packet_id)
        if event is None:
            _LOGGER.error("Invalid device: %s", packet_id)
            continue

So it’s the result of get_rfx_object. Taking a look at that:

def get_rfx_object(packetid):
    """Return the RFXObject with the packetid."""
    import RFXtrx as rfxtrxmod

    try:
        binarypacket = bytearray.fromhex(packetid)
    except ValueError:
        return None

The key line here is the creation of binarypacket from packetid. So what is packetid? For that we need to go back to the get_devices_from_config function, where it’s clearly coming from the Home Assistant config. An educated guess and a bit of searching proved that it was the device ID of the switch. The switch is configured as follows:

devices:
  7100a0041010000:
    name: "rfxswitch1"

The packetid being passed to get_rfx_object was 7100a0041010000. I had a play within the Python interpreter:

>>> binary = bytearray.fromhex("7100a0041010000")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: non-hexadecimal number found in fromhex() arg at position 14

There it is. The ValueError that was triggering the exception in get_rfx_object.

I wasn’t familiar with the fromhex() function, but a quick play with an obviously correct hex number showed that the hex string has to be an even number of digits:

>>> binary = bytearray.fromhex("a")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: non-hexadecimal number found in fromhex() arg at position 0
>>> binary = bytearray.fromhex("0a")
>>> 

My device ID was 15 digits long. At that point I decided to try updating my config to make it 16 digits by adding a leading zero:

devices: 
  07100a0041010000: 
    name: "rfxswitch1"

That did the job, the errors disappeared from the logs and my switches sprang back into life.

So everything was working but that didn’t explain why it broke in the first place. When something breaks like that it’s good to know exactly why it broke.

I did a bit of digging on Github to see the changes that had been introduced in 0.61. It turned out there had been a major tidying up of the RFXtrx code.

The pre-0.61 code passed the configuration through a validation function that validated the device IDs. The key lines in that function were:

        key = str(key)
        if not len(key) % 2 == 0:
            key = '0' + key

There it is – in versions up to 0.60, the Home Assistant code in rfxtrx.py was adding a leading zero to ensure that the IDs were an even number of digits.

It’s not really a bug. The older version was just more forgiving. You could argue that anything that breaks a previously working configuration is a bug, but the Home Assistant project never shies away from breaking changes if it makes for tidier code.

The point of this little case study is that open source software gives you the ability to track down issues in a way that just wouldn’t be possible with commercial software.

You don’t need to be an expert in Python to read the code – if you’ve done any programming in other languages you can probably follow what’s going on well enough. Even with my relatively limited experience with Python I was able to figure it out.

I wouldn’t say I’m ready to jump in and contribute changes, but I don’t need to be. If the problem had turned out to be a genuine bug that needed a code fix, I would’ve been able to log a far more detailed bug report than would have been possible without the source code.

Sometimes it’s only when you hit a problem and find a fix that you truly appreciate the benefit of the open source approach.

in Random Musings

Related Posts

2 Comments

  1. Jon Creasey 2nd February 2018
  2. seanbAuthor 2nd February 2018

Add a Comment

Your email address will not be published. All comments will be reviewed.