January 5, 2017

C.H.I.P, flic and openhab

So I finally received my C.H.I.P, and over the break I purchased myself some flic bluetooth buttons with the plan of making some nice push buttons around the home to control openhab. This idea mostly came from usablity/speed of use of openhab. Having the dashboard in the middle of the house is a nice idea, but if you are in another room, without your phone there isn’t too much you can do without having to go to this dashboard to interact with openhab. Flic makes it nice and convenient to do things you do often, with a click, double click or press of a button!

Some examples, incase this still seems silly to you.

  1. A button by the back door
    • A single click it will “turn off” the house. This includes lights, lamps and music from my mpd server.
    • double click on the way back in, and it will turn the main house lights back on for you
    • Long press to update openhab that you are now home. This then triggers some other openhab rules
  2. A button by my bedside table
    • Single click to turn bedside table lights on night mode (For those middle of the night wake ups)
    • Double click to turn the main house lights on (useful for winter when its still dark when I wake up)
    • Long press to turn all the lights off. For bed time
  3. A button by my wife’s bed side table
    • Single click to turn the hall way, and lounge room lights on night mode. For when she has to feed the baby
    • Double click to turn those back off
    • Long press to turn all lights off, for bed time
  4. Button on a clip that sits around the house
    • Single click to play music
    • Double click to pause music
    • Long press to skip song

This makes some everyday tasks much more convenient, without having to pull your phone out and load openhab (if you aren’t near the dashboard). The only con of this setup I have found so far is that, as it runs off bluetooth, the range of the buttons is quite limited if using the on-board bluetooth controller of C.H.I.P. To exend it nicely through your house, I recommend purchasing a usb bluetooth controller (I will update this once I find a suitable one for my setup)

Setup

So C.H.I.P comes preinstalled with a GNU/Linux debian based OS customised specifically for C.H.I.P. This is nice as I am very familiar with debian. Connecting to C.H.I.P was super easy. Plug into usb on a desktop and it presents itself as a serial connection you can then screen into! Their docs are quite well written so it was a breeze to follow

Flic have developed a linux sdk for working with the buttons, obviously aimed at developers, so you can extend the buttons further then using the smart phone apps. This is nice as instead of having to pair the buttons with your phone (which moves around all the time) you can create a dedicated devices in 1 place in the house to use and interact with flic. Anyway, some initial setup notes on connecting flic to C.H.I.P

#update the chip
sudo apt-get update
sudo apt-get upgrade

#Install some useful things

sudo apt-get install git
sudo apt-get install bash-completion screen

#Clone the flic linux sdk

git clone https://github.com/50ButtonsEach/fliclib-linux-hci.git

Now at this point I had no idea what to use to script up flic, but the SDK comes with some nice client librarys (and examples!) for C, Java, nodejs python and websocket. Out of this list, I have had the most experience with javascript lately , so I chose Nodejs. So lets install nodejs and npm

sudo apt-get install nodejs npm

So because I have an ISP that offers IPv6 too, I was running into an issue where apt was timing out connecting to a debian repository over IPv6, so I had to force apt to only use IPv4

sudo apt-get -o Acquire::ForceIPv4=true install nodejs npm

A weird thing you need to do for npm to make sure its up to date with the latest

sudo npm install -g npm

And we also need the http library for nodejs

sudo npm install -g http

Now we have all we to get things running! Just for now, we will start the server in a screen so we can see whats happening if needed. It also starts in daemon mode too, but we will use that one later to integrate with systemd

cd fliclib-linux-hci/bin/armv6l
./flicd -f sqlite.db

So the flic server will store all the information in needs into this sqlite db, make sure you have it in a safe location so you dont have to re-pair your buttons again! Now onto the client.

cd fliclib-linux-hci/clientlib/nodejs
nodejs newscanwizard.js

If you check your server screen it will also show that a “client” has connected to it

Now press one of your buttons and watch some magic! Save that address of the button for later, we will now write up a small client to handle the buttons and make them do things when you press them

vim nodejs-flic.js

#!/usr/bin/nodejs
var fliclib = require("/home/chip/Documents/fliclib-linux-hci/clientlib/nodejs/fliclibNodeJs");
var FlicClient = fliclib.FlicClient;
var FlicConnectionChannel = fliclib.FlicConnectionChannel;
var FlicScanner = fliclib.FlicScanner;
var http = require("http");
var client = new FlicClient("localhost", 5551);

function listenToButton(bdAddr) {
        var cc = new FlicConnectionChannel(bdAddr);
        client.addConnectionChannel(cc);
        //There are other methods you can watch, but all I care about at the moment is what type of click just happened
        cc.on("buttonSingleOrDoubleClickOrHold", function(clickType, wasQueued, timeDiff) {
                console.log(bdAddr + " " + clickType + " " + (wasQueued ? "wasQueued" : "notQueued") + " " + timeDiff + " seconds ago");
                //If statement to find out what button was pressed. Probably turn this into case to make it neater
                if(bdAddr === "ab:cd:ef:11:22:33"){
                        console.log("Blue button");
                        /*Create http request to openhab API
                        hostname: openhab server IP address
                        port: openhab port (default 8080)
                        path: Path to the item you wish to change.
                        */
                        var options = {
                                    hostname: '1.2.3.4',
                                    port: 8080,
                                    path: '/rest/items/all_off',
                                    method: 'POST',
                                    headers: {
                                            'Content-Type': 'text/plain'
                                    },
                            };
                            var req = http.request(options, function(res) {
                                    console.log('STATUS:' + res.statusCode);
                                    console.log('HEADERS:' + JSON.stringify(res.headers));
                                    res.setEncoding('utf8');
                                    res.on('data', function(body) {
                                            console.log('BODY:' + body);
                                    });
                                    res.on('error', function(e) {
                                            console.log('problem ' + e.message );
                                    });
                            });
                        if(clickType === "ButtonSingleClick"){
                                //On single click, send OFF to the openhab item
                                req.write("OFF");
                                req.end();
                        }else if(clickType === "ButtonDoubleClick"){
                                //On double click, send ON to the openhab item
                                console.log("doubelclick");
                                req.write("ON");
                                req.end();
                        }else if(clickType === "ButtonHold"){
                                console.log("hold");
                        }else{
                                console.log("Something Wrong");
                        }
                }
        });
        cc.on("connectionStatusChanged", function(connectionStatus, disconnectReason) {
                console.log(bdAddr + " " + connectionStatus + (connectionStatus == "Disconnected" ? " " + disconnectReason : ""));
        });
}

client.once("ready", function() {
        console.log("Connected to daemon!");
        client.getInfo(function(info) {
                info.bdAddrOfVerifiedButtons.forEach(function(bdAddr) {
                        listenToButton(bdAddr);
                });
        });
});

client.on("bluetoothControllerStateChange", function(state) {
        console.log("Bluetooth controller state change: " + state);
});

client.on("newVerifiedButton", function(bdAddr) {
        console.log("A new button was added: " + bdAddr);
        listenToButton(bdAddr);
});

client.on("error", function(error) {
        console.log("Daemon connection error: " + error);
});

client.on("close", function(hadError) {
        console.log("Connection to daemon is now closed");
});

As you can see in my terribly coded example, we are watching for a button press, and when a button press happens, we find out which button it was, what sort of press it was, and do an action. So in those gaps you can do anything you want! In my case I am just hitting the openhab rest API to action the items I have connected to openhab. This is where the SDK is nice, you can do anything you want here!

You may notice here that I only specify 1 openhab item that I am changing the state of. This is because I am doing the rest of the work via the openhab rules engine. The item in openhab is just a switch, that binds to nothing. The entry in default.items looks like this Switch all_off "All Off" You can also add this as a switch item to your sitemap, so you can run the rule below from the openhab UI. Here is the rule that handles this blue button (all_off item)

#/etc/openhab/configuration/rules/flic.rules
import org.openhab.core.library.types.*

rule "All off"
when
        Item all_off received command OFF
then
        sendCommand(L_light, 0)
        Thread::sleep(500)
        sendCommand(K_light, OFF)
        Thread::sleep(500)
        sendCommand(E_light, OFF)
        Thread::sleep(500)
        sendCommand(H_light, OFF)
        Thread::sleep(500)
        sendCommand(B_light, OFF)
        Thread::sleep(500)
        sendCommand(BT_light, OFF)
        Thread::sleep(500)
end

rule "Home on"
when
        Item all_off received command ON
then
        sendCommand(K_light, ON)
        Thread::sleep(500)
        sendCommand(L_light, 100)
        Thread::sleep(500)
        sendCommand(BT_light, ON)
        Thread::sleep(500)
        sendCommand(K_light_dim, 100)
        Thread::sleep(500)
end

I have all these sleep commands in here, as I found that having the commands run directly after one another did not work well with milight, so adding a small delay made sure each one runs successfully and the milight bridge actually gets the command

Now that we have a nice peiece of code that handles our buttons and actions, I didn’t want these to be running in screen sessions. you know, in case of an outage or similar, I didnt want to have to ssh into C.H.I.P to start everything back up again. systemd is nice here. We can write some stuff in a file so systemd can handle running the code as a service for us, just like any other daemon you have installed.

vim flic.service

[Unit]
Description=Flic and openhab

[Service]
ExecStart=/home/chip/Documents/fliclib-linux-hci/clientlib/nodejs/nodejs-flic.js
Restart=always
User=root
Group=root
Environment=PATH=/usr/bin:/usr/local/bin
Environment=NODE_ENV=production
WorkingDirectory=/home/chip/Documents/fliclib-linux-hci/clientlib/nodejs/

[Install]
WantedBy=multi-user.target

Make sure you change the user here, and make the .js file executable! I did not, and had some errors.

chmod +x /home/chip/Documents/fliclib-linux-hci/clientlib/nodejs/nodejs-flic.js

Now copy it to where systemd exects these files

sudo cp flic.service /etc/systemd/system/

Start it

sudo systemctl start flic.service

check logs

sudo journalctl -fa -u flic.service

This acts like tail -f on the log file. Now its running under systemd you can add this service to start at startup, and manage it using the systemctl command. Now we just do a similar thing to make the flic daemon managed by systemd