HomeKit is finally out in the wild, bringing Siri voice control to several consumer smart home devices.

Unfortunately, I mean a literal handful — everything you’ve already bought is probably not compatible. However, the protocol has already been reworked and an open source emulator for the HomeKit API is available: or in simple terms, you can now create «fake» HomeKit devices and have Siri control them like any other official HomeKit accessory.

Today we’re going to create a Wi-Fi controlled light source and control it with Siri. Here’s a demo.

Here’s what you need:

  • Raspberry Pi (I used RPi2, there is a slight difference in Node versions to install given the updated ARM architecture — see notes later).
  • MQTT broker installed on Raspberry Pi. See Installing Mosquitto on the Pi in my OpenHAB Guide Part 2, It doesn’t need to be installed specifically on the Pi — you can even use an MQTT cloud server, but since we need a Pi for this tutorial anyway, it’s handy.
  • NodeMCU v2 (Arduino compatible)
  • Non-pixel LEDs (I would recommend 4 pixels for testing, then you can add an external power supply and add as many as you like)

Installing the HomeKit Bridge

We are going to install a NodeJS application called HAP-NodeJS on Raspberry Pi: this will form a bridge between HomeKit requests and Wi-Fi devices. We will now set up this bridge with one accessory, but you can add as many as you like.

I’m actually installing this on my existing home server running OpenHAB — I’m hoping to link them together later, but for now I know they can coexist on the same Raspberry Pi. If you do the same, make a backup of your current Pi SD card just in case. If things go wrong, you can restore this.

Start with a full refresh from a terminal or SSH session. .

  sudo apt-get update
 sudo apt-get upgrade

You may need to do this twice if it’s been a while.

Now install a few basic packages that we will need:

  sudo apt-get установить npm git-core libnss-mdns libavahi-compat-libdnssd-dev

Next, we are going to install the latest version of NodeJS. You might be tempted to do this with apt-get but don’t do it — this version is really old and won’t work. Instead, visit nodejs.org , navigate to the directory download/release/latest-v5.x.0/ and check which link is for the latest version. You are looking for linux-armv7l for Raspberry Pi 2 or linuxarmv6l for original RPi models. Then, modifying the URLs and directory names as necessary, download and install using the following commands.

  wget https://nodejs.org/download/release/latest-v5.x.0/node-v5.5.0-linux-armv7l.tar.gz
 tar -xvf node-v5.5.0-linux-armv7l.tar.gz
 cd node-v5.5.0-linux-armv7l
 sudo cp -R * / usr / local

Confirm by typing

  версия узла

And you should see v5.5 (or whatever the latest you downloaded).

Next, we have a few Node modules to install.

  sudo npm install -g npm
 sudo npm install -g node-gyp

In this first command, we are actually using the Node Package Manager (npm) to install the newer version. Clever!

Now to download the HomeKit emulator called HAP-NodeJS :

  git clone https://github.com/KhaosT/HAP-NodeJS.git
 cd HAP-NodeJS
 нпм перестроить
 sudo npm install node-persist
 sudo npm установить srp

At this point, I ran into this error: » #error This node/NAN/v8 version requires a C++11 compiler «. If this happens to you, install a more recent C++ compiler with the commands:

  sudo apt-get установить gcc-4.8 g ++ - 4.8
 Возможности обновления sudo --install / usr / bin / gccgcc / usr / bin / gcc-4.6 20
 Возможности обновления sudo --install / usr / bin / gcc gcc /usr/bin/gcc-4.8 50
 Возможности обновления sudo --install / usr / bin / g ++ g ++ /usr/bin/g++-4.6 20
 Возможности обновления sudo --install / usr / bin / g ++ g ++ /usr/bin/g++-4.8 50

Now you shouldn’t have any problems. Continue to run these commands, one by one:

  sudo npm установить srp
 sudo npm install mdns --unsafe-perm
 sudo npm install debug
 sudo npm install ed25519 --unsafe-perm
 sudo npm install curve25519 --unsafe-perm

This should be everything. Try starting the emulator with:

  узел Core.js

If you get messages that it can’t find such-and-such a module, simply enter command sudo npm install and add the name of the module that was missing. Assuming all is well, you should see a few warnings and your HomeKit bridge should work. Here’s what success looks like:

installed hap-nodejs

It is immediately clear that a set of 6 fake devices has already been created. We’ll use them as a starting point for our own Wi-Fi indicator later, but for now, we’ll just use them for testing purposes. You can also see more debug information if you start the server with:

  DEBUG = * узел Core.js 

Now navigate to an Apple device capable of working with Siri. Curiously, Apple doesn’t provide a stock HomeKit app other than registered developers, so download the free Elgato Eve app, a HomeKit management app that lets you add (even non-Elgato) devices to your HomeKit network.

The first time you launch the app, you will need to name your home, walk through it, and walk through it. Then select Add Accessory. Ignore the message about being close to it!

Elgato Eva 1

Next, you will be prompted to find a unique «HomeKit Installation Code». Ignore this and click «Add to [название вашего дома]».

It will also tell you that the device is not certified. Actually it is not. Go ahead anyway. When you get to the screen asking for a passcode…

Elgato Eva 2

Select to enter the code manually and enter the following:


This can be found/changed in the file light_accessory.js but more on that later. Add this accessory to your default room, name it fake light and keep going through the dialog boxes to select an icon, etc.

Finally, switch back to the SSH session where you have HAP-NodeJS running. You may have already seen the message “Are we online?” is an Elgato application polling light status. Open Siri and tell her, «Turn on the fake light,» then try turning it off again. Hopefully you will see some debug messages from HAP-NodeJS to show that it has received the commands.

  Мы находимся?  Нет.
 Включение света!
 Выключить свет! 

Fantastic, this first step is finished. Now we need some real light before we go back to set up the bridge again.

Creating Wi-Fi Light

The hardware side of this step is surprisingly simple if we start with four neo-pixels since we can power them directly from the NodeMCU dev board and its USB connection. If you have a longer bar, don’t worry — we have determined this in the software, so the rest simply won’t turn on.

Connect the red power cable from the Neopixel core to the VIN pin, the blue ground to GND, and the green signal cable to the pin labeled D2 on the NodeMCU. Be very careful with polarity: if you mix up ground and VIN, you will send a powerful pulse through your board and destroy it in the process.

If your Arduino environment is not yet set up to work with the ESP8266, go ahead and follow the guide in my ESP8266: The Arduino Killer guide and then come back after you’ve confirmed it works. Install these additional libraries:

  • lmroy’s PubSubClient
  • Non-Apixels Adafruit

The code we’re using is a modification of Github user Aditya Tannu — I’ve removed unnecessary over-the-air update features, added some missing HSV features, and made it easier to create more lights by just changing one variable If you don’t see the code embedded below, you will find it in this list.

#include <ESP8266 WiFi.h>
#include <WiFiClient.h>
#include <ESP8266mDNS.h>
#include <WiFiUdp.h>
#include <PubSubClient.h>
#include <Adafruit_NeoPixel.h>
#define PIN four
Adafruit_NeoPixel strip = Adafruit_NeoPixel(fourPIN, NEO_GRB + NEO_KHZ800);
const char*ssid= «….«;
const char*password= ««;
const char*host= «office light«; // the name of your fixture, and the base channel to listen to
IPAddress MQTTserver(192, 168, one, 99);
int hue = 0;
float brightness= 0.0;
float saturation = 0.0;
#define BUFFER_SIZE 100
WiFiClient wclient;
PubSubClient client(wclient, MQTTserver);
void callback(const MQTT::Publish& pub) {
uint16_t i, j;
String myMessage = String(pub.payload_string());
// handle message arrived
Serial.print(« => «);
String myTopic = String(pub.topic());
if(myTopic == host)
if(pub.payload_string() == «on«)
// use this to reset parameters if you want them to always come on bright white.
//hue = 0;
brightness= 1.0;
//saturation = 0.0;
for(i=0; inumPixels(); i++) {
strip.setPixelColor(i, HSV Color(hue,saturation,brightness));
//hue = 0;
brightness= 0.0;
//saturation = 0.0;
for(i=0; inumPixels(); i++) {
strip.setPixelColor(i, HSV Color(hue,saturation,brightness));
else if(myTopic == host+(String)«/brightness«)
{ // Brightness up to 100
brightness = (myMessage.toFloat())/100;
for(i=0; inumPixels(); i++) {
strip.setPixelColor(i, HSV Color(hue,saturation,brightness));
else if(myTopic == host+(String)«/hue«)
{ // Hue value 0-360
hue = myMessage.toInt();
for(i=0; inumPixels(); i++) {
strip.setPixelColor(i, HSV Color(hue,saturation,brightness));
else if(myTopic == host+(String)«/saturation«)
{ // Saturation value at 0-100
saturation = (myMessage.toFloat())/100;
for(i=0; inumPixels(); i++) {
strip.setPixelColor(i, HSV Color(hue,saturation,brightness));
void setup() {
WiFi.begin(ssid, password);
while (wifi.waitForConnectResult() != WL_CONNECTED) {
Serial.println(«Connection failed! Rebooting…«);
Serial.print(«IP address: «);
Serial.println(wifi.local IP());
// MQTT callback
strip.show(); // Initialize all pixels to ‘off’
void loop() {
if (wifi.status() == WL_CONNECTED) {
if (!client.connected()) {
if (client.connect(«ESP8266: Fountain«)) {
client.publish(«outTopic«,(String)«hello world, I’m «+host);
if (client.connected())
// Convert Hue/Saturation/Brightness values ​​to a packed 32-bit RBG color.
// hue must be a float value between 0 and 360
// saturation must be a float value between 0 and 1
// brightness must be a float value between 0 and 1
uint32_t HSV Color(float h, float s, float v) {
h = constraint(h, 0, 360);
s= constraint(s, 0, one);
v= constraint(v, 0, one);
int i, b, p, q, t;
float f;
h /= 60.0; // sector 0 to 5
i = floor(h);
f = h — i; // factorial part of h
b=v* 255;
p = v * ( one -s)* 255;
q = v * ( one -s*f)* 255;
t = v * ( one -s*( one — f ) ) * 255;
switch( i ) {
case 0:
return strip.color(b, t, p);
case one:
return strip.color(q, b, p);
case 2:
return strip.color(p, b, t);
case 3:
return strip.color(p, q, b);
case four:
return strip.color(t, p, b);
return strip.color(b, p, q);
void currentValues(){
Serial.println(«current state«);
Serial.print(«Hue (0-360):«);
Serial.print(«Saturation (0-100in, 0-1):«);
Serial.print(«Brightness (0-100):«);

view raw
hosted with ❤ by GitHub

Update the following lines with your own network information and a unique name for each instrument (host) you create.

  const char * ssid = "....";
 const char * password = "...";
 const char * host = "officelight"; 
 IP-адрес MQTTserver (192, 168, 1, 99); 

The IP address of this device is automatically obtained via DHCP — it doesn’t matter if it changes as we are connecting to the same MQTT server every time.

At the moment we are only using 4 Neopixels, but you can increase the number later if you feed them from an external source. Download the code and let’s check it out — use your favorite MQTT client to send commands (change the hostname in the following instructions if you have changed it) .

  • You can send it to root office channel to enable it. Send any other value to this channel to disable it.
  • You can send a number from 0 to 360 to the office light/tint to change the color. We’re using the HSV color space, so 0 and 360 are red, 120 are green, and 240 are blue.
  • You are sending a percentage value for brightness (0-100, not including the % character).
  • The same for saturation. A value of 100 will be fully saturated (i.e., a solid color) and zero will be pure white, regardless of the specified hue.

Once you have verified that the MQTT powered light fixture is working, continue.

Setting up a new HomeKit accessory

Return to your Raspberry Pi and close the HAP-NodeJS application if you haven’t already. Go to directory / accessories . To make this easier, you can directly download the code already associated with the «officelight» fixture by typing the following:

  wget https://gist.githubusercontent.com/jamesabruce/a6607fa9d93e41042fee/raw/12e4fd1d1c2624e7540ba5e17c3e79bc6bdec5fd/Officelight_accessory.js 

This is essentially a duplicate of the standard light accessory with the variable names changed (again, adapted from Adisan’s work, simplified for ease of use). Here’s what you need to know to create your own accessories based on this.

You will need to add the new accessory again via the HomeKit app.

Finally, we want to run our HAP-NodeJS application every time we restart the Pi. Add the following to your file etc/rc.local right in front of exit 0 .

  узел sudo /home/pi/HAP-NodeJS/Core.js  

You can see that I’ve combined this with some of the other commands I’ve already configured to run on boot.

rclocal run node on startup

If you are using rc.local for the first time, you may need to install it as an executable:

  sudo chmod 755 /etc/rc.local 

If for some reason you need to run it again in debug mode, you can kill the running Node app with:

  узел killall 

Last step: go to accessories directory and remove GarageDoorOpener_accessory.js . At the time of writing, this is a bug and the server may break after some time.

What will you control with Siri?

Now that you’ve mastered the basics, there’s really no limit to what you can control — if you can code it in Javascript, you can create your own helper file. There are so many possibilities here, I think you will have a lot of fun. Let me know in the comments what you came up with!

Похожие записи