Introduction

Having built a HomeKit compatible light1 I thought it was time to build a sensor too. Rather than build a finished product, I wanted a proof-of-concept to test reliability and convenience. So, the biggest design concern was simplicity and speed of construction.

One slightly longer term project is to automate the lights at home, so that they come on when it gets dark. Some sort of ambient light sensor seems a key part of this. I am also quite interested to monitor the environment at home, so ideally I’d like temperature and humidity sensors too.

The Enviro pHAT

To keep things simple, I wanted to use off-the-shelf hardware and so acquired an Enviro pHAT2 board from Pimoroni. This board lacks a humidity sensor, but has a convenient python library to simplify the software. The board also measures motion and barometric pressure, but I’m ignoring these for now.

Happily Pimoroni provide a nice python library which talks to the board3.

Temperature sensing is done by the BMP2804 pressure sensor, which isn’t ideal. The data sheet says:

Temperature measured by the internal temperature sensor. This temperature value depends on the PCB temperature, sensor element self-heating and ambient temperature and is typically above ambient temperature.

Besides these issues, the sensor is reasonably close to the CPU on the Pi which gets quite warm. The net effect of all this is that the sensor reads significantly high: as I write this a thermometer reads about 22°C, the BMP280 about 30°C. Although these problems could be mitigated by calibrarion, for real work a different sensor placed some distance from the Pi is probably warranted.

Light is sensed by a TCS34725. This is a full RGB sensor, but I only use the total brightness value. The HomeKit documentation says I should provide a value in lux: I am just using the number returned by the python library. Subjectively, I want to turn on the lights when the level falls to about 100.

Software

You can grab all the code from GitHub6.

As with the light, Ivan Kalchev’s HAP-python7 library handles all the HomeKit stuff. Thank you again Ivan!

The main code for the accessory is shown below:

from pyhap.accessory import Accessory
from pyhap.const     import CATEGORY_SENSOR

from envirophat import light, weather

class Ephat(Accessory):

    category = CATEGORY_SENSOR

    def __init__(self, driver, *args, **kwargs):
        super().__init__(driver, *args, **kwargs)

        chars = { 'LightSensor':
                  [ ( 'CurrentAmbientLightLevel',  lambda: light.light() )
                  ]
                  ,
                  'TemperatureSensor':
                  [ ( 'CurrentTemperature', lambda: weather.temperature() )
                  ]
                  ,
                  'Switch':
                  [ ('On', lambda: light.light() < 100)
                  ]
                }

        self.chars = []
        for sname, charlist in chars.items():
            cnames = [ name for (name,_) in charlist ]
            service = self.add_preload_service(sname, chars = cnames)

            for (name, getter) in charlist:
                c = service.configure_char(name)
                self.chars.append((c, getter))

    @Accessory.run_at_interval(3)
    def run(self):
        for (char, getter) in self.chars:
            v = getter()
            char.set_value(v)

The local chars dictionary defines all the sensors. We walk this structure both to initialize the HAP Accessory, and to compile a list of characteristics and callbacks in the Accessory’s chars property.

Note that the names for the services and characteristics e.g. LightSensor and CurrentTemperature must match the official Apple standards. You can’t just invent your own (which is why I didn’t add a characteristic for atmospheric pressure).

You will also see that I’ve added a virtual Switch characteristic which turns on when the light level falls below a threshold. This makes it easier to automate things in the Home app: just tell the light to come on which the Switch closes. I really should add other virtual switches with slightly different thresholds so that different lights turn on at slightly different times.

Finally, we set up a periodic task (here every three seconds) to make new measurements and update the Accessory’s state.

Walkthrough

The notes below assume you’ve set up the Pi roughly along these lines8.

The Enviro pHAT sensors communicate with the Pi over the I²C bus, so you’ll need to enable that e.g. by running raspi-config.

Now install the dependencies:

$ sudo apt-get install libavahi-compat-libdnssd-dev git python3-envirophat
$ pip3 install HAP-python

You will notice that HAP-python is installed without QR-code support. Although QR codes are convenient if you attach the accessory to your Home from the command line, the QR code is unreadable in system logs (and makes a real mess of them).

Once python3-envirophat has been installed you can use Pimoroni’s test code to check that the hardware is working:

$ wget https://raw.githubusercontent.com/pimoroni/enviro-phat/master/examples/all.py
$ python3 all.py

Finally grab the Accessory code from GitHub, and run it:

$ git clone https://github.com/mjoldfield/mjo-homekit.git
$ cd mjo-homekit/code
$ python3 ephat.py

You should then be able to add the Accessory in the Home app on your iPhone.

systemd

If you want to start all this automatically, you can use the systemd script in mjo-homekit/systemd/ephat.service.

Discussion

In essence, that’s all there is to this. The sensor works reliably with a minimum of fuss. Clearly things could be improved: it would be nice to collect more data and with better fidelity.

There is one issue: sometimes the sensor gets ‘stuck’ when powered down and then restarted. You can follow this on GitHub9.