DIY Hue scenes in Home Assistant using the deCONZ integration

2020-04-27

The Philips Hue app offers a wide variety of scenes a user can pick from to assign predefined colors to a group of bulbs. I also used this application (the bluetooth version) before I started using Home Assistant. The application is not really useful once you start using Home Assistant because you want to manage everything from within the system. That's why I decided to recreate all those scenes provided by the app in Home Assistant using as much automation as possible because it would be a very tedious process otherwise.

Decompile

The first step in the process was to decompile the android app to get access to the images they used, I used a tool called Apktool for this. I've decompiled some apps before in the past but this happened a long time ago, in the end I managed to find the images, the code is somewhat hard to read but that doesn't matter as I am not interested in it.

The images are all in webp format and are all coming from Unsplash which means they can be used for commercial and noncommercial purposes, perfect for Home Assistant.

deCONZ

deCONZ by dresden elektronik is a software that communicates with ConBee/RaspBee Zigbee gateways and exposes Zigbee devices that are connected to the gateway.

- https://www.home-assistant.io/integrations/deconz/

You can find the deCONZ add-on in the add-on store

In the settings of the add-on you need to enable the REST API, which we will need later on when we are configuring the scenes for our bulbs. I just kept the same port the container uses: 40850.

Code

I decided to create a small library to help automate the creation of scenes based on a set of images.

Color Thief

Grab the color palette from an image using just Javascript.Works in the browser and in Node.

- https://github.com/lokesh/color-thief

Using this utility I can generate a color palette based on an image and a set amount of bulbs as a parameter. It can look something like this:

cie-rgb-color-converter

A simple JS lib for converting rgb to cie1931 (Which Philips HUE Color lights use) and vice versa.

- https://github.com/Shnoo/js-CIE-1931-rgb-color-converter

The Hue bulbs and deCONZ both use the CIE 1931 color space, that's why we need this library to convert the RGB colors we get from the palette.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const ColorThief = require('colorthief');
const ColorConverter = require('cie-rgb-color-converter');

const fs = require('fs');
const axios = require('axios');

const imagesFolder = './images/';
const IP = "YOUR_OWN_IP_KEY_HERE";
const PORT = YOUR_OWN_PORT_KEY_HERE;
const API_KEY = "YOUR_OWN_API_KEY_HERE";

const DEFAULT_BRIGHTNESS = 150;
const DEFAULT_TRANSITION_TIME = 5;

const LIVING_ROOM_GROUP = 9;
const LIVING_ROOM_CEILING_1 = 12;
const LIVING_ROOM_CEILING_2 = 13;
const LIVING_ROOM_CEILING_3 = 14;

const bulbs = [
    LIVING_ROOM_CEILING_1,
    LIVING_ROOM_CEILING_2,
    LIVING_ROOM_CEILING_3
];
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
const getFiles = () => {
    const result = [];
    fs.readdirSync(imagesFolder).forEach(file => {
        result.push(file);
    });
    return result;
};

const buildColorInfo = (file) => {

    const sceneName = getSceneName(file);
    const imagePath = imagesFolder + file;

    ColorThief.getPalette(imagePath, bulbs.length)
              .then(palette => {
                  const colorSpace = buildCIEColorSpace(bulbs.length, palette);
                  createScene(sceneName, colorSpace);
              })
              .catch(err => {
                  console.log(err)
              });
};

const createScenes = async () => {
    const files = getFiles();
    for (let i = 0; i < files.length; i++) {
        buildColorInfo(files[i]);
        await sleep(500);
    }
};

The sleep of 500ms in the createScenes method above is needed because you will get errors when sending requests too fast to the deCONZ add-on.

1
2
3
4
5
6
7
8
9
10
11
12
13
const createScene = (sceneName, colorSpace) => {
    axios.post(`http://${IP}:${PORT}/api/${API_KEY}/groups/${LIVING_ROOM_GROUP}/scenes`, {
        "name": sceneName
    })
         .then((res) => {
             const id = res.data[0].success.id;
             console.log(`New scene "${sceneName}" has been created with ID: ${id}`);
             modifyScene(id, colorSpace);
         })
         .catch((error) => {
             console.error(error)
         });
};

Create new scenes by making a POST request, you will get the ID of the newly created scene which you will need later on.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const modifyScene = async (sceneId, colors) => {
    for (let i = 0; i < bulbs.length; i++) {
        axios.put(`http://${IP}:${PORT}/api/${API_KEY}/groups/${LIVING_ROOM_GROUP}/scenes/${sceneId}/lights/${bulbs[i]}/state`, {
            "bri": DEFAULT_BRIGHTNESS,
            "on": true,
            "transitiontime": DEFAULT_TRANSITION_TIME,
            "xy": colors[i]
        })
             .then((res) => {
                 console.log(res.data);
             })
             .catch((error) => {
                 console.error(error)
             });
    }
};

For each bulb we need to make a PUT request with the details of that individual bulb. The data includes brightness, on or off state, transition time (which is expressed in 1/10th of a second for some reason) and CEI color information.

The scenes are now available in deCONZ and are ready to be called by Home Assistant

Demo

If you have any questions, do not hesitate to contact me or leave a comment below.

Created by Jeroen Druwé