Skip to content
Colin Wren
Twitter

Visualising test results with PyTest and Philips Hue

Python, Testing, Technology, Software Development4 min read

test reporter changing light colour
I built a test reporter for PyTest that controls my Philips Hue lamp based on the test results

A couple of months ago I invested in a Philips Hue lamp and bridge, shortly after I got two Play lights for the back of the TV (in time for binging Game Of Thrones before the last season came out).

I like the idea of having the lighting controlled by changes such as the image on the TV, getting warmer in the evening or turning green and playing Smashmouth when an intruder breaks into the house.

My initial plan was to hook into the local Pokemon Go map and use the current team that owns the Pokemon Gym near our flat to control the colour of our lights — allowing the real world to be controlled by actions in the game and adding another level of augmentation to the players reality.

Unfortunately I rent so I’m not really in a position to set up something elaborate but I knew I wanted to play around with the lamp I had and at the time I had just finished using PyTest for configuration testing so thought a light based test reporter would be a good place to start.

Controlling your lights with Python

Philips Hue provides a really well implemented API that on following the developer introduction provided by Philips you learn how to register a new app locally and how to query and set the state of the devices and lights connected to the Phillips Hue Bridge.

Philips also provide a list of libraries for various languages on their developer website which is really useful.

As I’m working with PyTest I needed a library for Python and having reviewed the offerings on both Philips list and using the philips-hue tag on Github I settled on PyHue as it provided the cleaned API for interacting with Philips Hue out of the bunch.

One issue with PyHue however is that running it on Python 3.7 resulted in a max recursion error on trying to set up the connection to bridge so I had to settle on running with Python 2.7 — I put this down to the fact that the last update to the library was 2013 but I am hoping to contribute code to make it Python 3 compatible.

In order to control your lights you need two pieces of information:

  • The IP address of your Philips Hue Bridge
  • A username for an app (which can be created following the Philips Developer tutorial)

You then create a new instance of pyhue.Bridge which you can then query for the rooms and lights connected to the bridge.

An example script to connect to the bridge, and turn all lights off that are in the ‘Office’ room would look like:

1import pyhue
2
3HUE_IP = 'XXX.XXX.XXX.XXX'
4HUE_USERNAME = 'replace_with_username'
5HUE_ROOMS = ['Office']
6
7# Get the pyhue.Bridge instance
8bridge = pyhue.Bridge(HUE_IP, HUE_USERNAME)
9
10# Find the room in the list of rooms set up by the user
11rooms = [room for room in bridge.groups if room.name in HUE_ROOMS]
12
13# Iterate over rooms
14for room in rooms:
15 # Iterate over lights in room
16 for light_id in room.lights:
17 # Use the light_id to find the light
18 light = bridge.get_light(light_id)
19 # Turn it off
20 light.on = False
If you don’t have rooms set up you can use bridge.lights() to get all lights

Controlling the brightness and colour

Once you’ve got your light instance you can then change the brightness using light.bri and the colour using X & Y values on a spectrum (which PyHue has a helper function for — rgb2xy(red, green, blue) .

The brightness and colour value range are from 0 to 255 so setting a red light at full brightness would look like:

1import pyhue
2
3HUE_IP = 'XXX.XXX.XXX.XXX'
4HUE_USERNAME = 'replace_with_username'
5HUE_ROOMS = ['Office']
6
7# Get the pyhue.Bridge instance
8bridge = pyhue.Bridge(HUE_IP, HUE_USERNAME)
9
10# Find the room in the list of rooms set up by the user
11rooms = [room for room in bridge.groups if room.name in HUE_ROOMS]
12
13# Iterate over rooms
14for room in rooms:
15 # Iterate over lights in room
16 for light_id in room.lights:
17 # Use the light_id to find the light
18 light = bridge.get_light(light_id)
19 # set the brighness
20 light.bri = 255
21 # use rgb2xy to get the XY value for red
22 light.xy = pyhue.rgb2xy(255, 0, 0)

Building a test reporter in PyTest

You can create a test reporter in PyTest via two ways:

  • Creating a new Python module with a _reporter.py which uses the PyTest hooks to add the reporter to the PyTest run
  • Adding the reporter to conftest.py (this is mostly for development purposes)

In both cases, the hooks you use to get data from the test run are:

  • pytest_collection_modifyitems(config, items) — This is called at the start of the test run and is where you can access information on the tests that will be run
  • pytest_runtest_logreport(report) — This is called during the setup, running and teardown of the test (`report.when` tells you which stage is currently being executed, call is the value for when the test is being run)

There are more hooks you can use to access the test report data at various stages but in order to show progress the two hooks mentioned will give you what you want.

Adding configuration values

In order to allow the user to provide values to configure the test reporter you need to create a _plugin.py file with the following hooks:

  • pytest_addoption(parser) — This is used to add the pytest command arguments so the values can be extracted
  • pytest_configure(config) — This is used to get the configuration values and then call config.pluginmanager.register() with an instance of the test reporter class you created using the configuration values
  • pytest_unconfigure(config) — This is used to call config.pluginmanager.unregister() to remove the test reporter

Calculating test run progress

Getting the test run progress is pretty simple, you can set the total number of expected test cases to be run in the pytest_collection_modifyitems(config, items) hook by calling len() on the items parameter.

Then in pytest_runtest_logreport(report) check that the function’s executed during the actual run of the test via report.when == 'call' and increment a test counter.

The call phase is also when the test result is available so you can increment counters for passed, failed, skipped tests too.

If you’ve created a class for your test reporter and you set a total tests and executed tests class property you’re able to get the total progress.

Using the test run progress to set light parameters

If you’re logging the total executed, passed, failed, skipped test counts then you can apply this formula to get a 0–255 value which can then be used by PyHue to control the lights.

1int(255 * (executed_tests / total_tests))

Demo

Here’s a video of the test reporter in action:

A demonstration of the PyTest-Hue test reporter

You can find the test reporter code on my Gitlab or you can download the package into your project via pip.

1pip install pytest-hue

In order to run the test reporter you’ll need to configure the following values:

  • --hue-ip=XXX.XXX.XXX.XXX — The IP address of Philips Hue Bridge
  • --hue-username=username_to_use — The username you created during the developer tutorial
  • --hue-rooms=room_name — A list of rooms to control

Next Steps

A couple of years back I experimented with Python and MIDI files and I think controlling my lights based on the MIDI data would be a nice little project to expand my Philips Hue knowledge.