How To Make Siri your Perfect Home Companion With Devices not Supported by Apple Homekit

Why Homekit?

Homekit is a home accessories management framework developed by Apple.
It allows Apple devices’ owners to control connected objects from different manufacturers using a single interface. It enhances Siri’s capability to interpret commands intended for those devices.

Homekit is particularly interesting, over other connected objects protocols like Home Assistance, if you own an iPhone and an AppleTV. Homekit is native on iPhone, allowing easy control of your appliances through Home app and quick access tab. The apple TV will behave as a hub allowing you to set up automation tasks and to control your home from outside of your home network.

How does it work?

Homekit Accessory Protocol

Homekit defines a layout for your home and your connected objects.

  • Home: A home represents a single dwelling that has a network of accessories
  • Room: Each home may have multiple rooms and accessories added to each room.
  • Platform: A group of accessories.
  • Accessory: An accessory is a physical home automation device.
  • Bridge: A bridge is a special type of accessory that allows you to communicate with accessories that can’t communicate directly with HomeKit. For example, a bridge might be a hub for multiple lights that use a communication protocol other than HomeKit Accessory Protocol.
  • Service: A service correspond to an accessory’s function. A garage door may have a service to open and close the door as well as another service to turn on and off the garage light.
  • Characteristic: Each service has a set of properties called characteristics. The garage door has a Current Door State and a Target Door State boolean. Each characteristic of a service identifies its current state. Each characteristic has 3 permission levels: read, write and notify. You can find a list of services and associated characteristics here.

Each request made using your iOS devices Home application or Siri will use this layout to understand which object you want to act on and what action you would like to trigger.

However, as of today, only a small number of Homekit enabled devices are available on the market. For other devices, you need a proxy between Homekit and your device. Most connected object manufacturers define their own way to interact with their devices (API and protocols). Your proxy will receive Homekit requests and translate them according to your device interface.


The proxy used for this article is a NodeJS server called Homebridge written using HAP-node.js. Homebridge instantiate a Bridge Homekit object that you will be able to add through your Home application on your iOS devices. It then supports Plugins, which are community-contributed modules that provide a basic bridge from HomeKit to each of your various “smart home” devices.
Many home automation devices plugins have already been developed by the community (like Nest, Lifx and even all of Home Assistant compatible devices).
If no plugin is available today for your object, this tutorial is made for you.


Writting your own plugin


  • You need to have Homebridge installed and running on any device of your LAN. You can follow these instructions.
  • You need to add Homebridge as an accessory to your Home application on iOS.


Let’s code a plugin for a fake switch.

Create a new repository containing a package.json file to manage our dependancies, and a index.js file that will contain our plugin core logic.

We will made the following assumption regarding our switch API:

  • it can be controlled through a RESTful API over HTTP protocol on our LAN
  • the switch IP address on our LAN is
  • GET requests made to /api/status returns a boolean representing switch current state. Doing so will read the On characteristic of the switch
  • POST requests made to /api/order containing a boolean representing the switch target state will trigger the corresponding action. Doing so will set the On characteristic of the switch

We will create a Homebridge plugin registering a new Accessory with two services:

  • AccessoryInformation service, required for every accessory, whatever the type, broadcasting information related to the device itself
  • Switch service, corresponding to our actual switch. Such service has a single On boolean required characteristic (check the list of services and corresponding characteristics)

First, we need to inject our plugin within homebridge.
mySwitch is the javascript object that will contain our control logic.

const Service, Characteristic;

module.exports = function (homebridge) {
  Service = homebridge.hap.Service;
  Characteristic = homebridge.hap.Characteristic;
  homebridge.registerAccessory("switch-plugin", "MyAwesomeSwitch", mySwitch);

The core logic built within HAP-node.js and Homebridge is located wihtin the getServices prototype function of mySwitch object.
We will instanciate our services in this function. We will also define which getter and setter of each characteristic of each service it shall call on every requests received from Homekit.

We need to instanciate :

  • an AccessoryInformation service containing:
    • a Manufacturer characteristic
    • a Model characteristic
    • a SerialNumber characteristic
  • a Switch service containing:
    • an On characteristic – the only required characteristic of this service

Unlike AccessoryInformation service’s characteristics, which are readable and can be set at plugin initialization, the On characteristic is writable and require a getter and setter.

mySwitch.prototype = {
  getServices: function () {
    let informationService = new Service.AccessoryInformation();
      .setCharacteristic(Characteristic.Manufacturer, "My switch manufacturer")
      .setCharacteristic(Characteristic.Model, "My switch model")
      .setCharacteristic(Characteristic.SerialNumber, "123-456-789");

    let switchService = new Service.Switch("My switch");
        .on('get', this.getSwitchOnCharacteristic.bind(this))
        .on('set', this.setSwitchOnCharacteristic.bind(this));

    this.informationService = informationService;
    this.switchService = switchService;
    return [informationService, switchService];

We will now write the logic of On characteristic getter and setter within dedicated prototype function of mySwitch object.
We will make the following assumption regarding the RESTful API offered by the switch :

  • GET requests on returns a { currentState: } reflecting the switch current state
  • POST requests on sending a { targetState: } reflecting desired target state set the switch state

We will use request and url modules to perform our HTTP requests.

Our configuration object, defined within Homebridge global configuration JSON, will contain both URLs described above.

const request = require('request');
const url = require('url');

function mySwitch(log, config) {
  this.log = log;
  this.getUrl = url.parse(config['getUrl']);
  this.postUrl = url.parse(config['postUrl']);

mySwitch.prototype = {

  getSwitchOnCharacteristic: function (next) {
    const me = this;
        url: me.getUrl,
        method: 'GET',
    function (error, response, body) {
      if (error) {
        me.log('STATUS: ' + response.statusCode);
        return next(error);
      return next(null, body.currentState);
  setSwitchOnCharacteristic: function (on, next) {
    const me = this;
      url: me.postUrl,
      body: {'targetState': on},
      method: 'POST',
      headers: {'Content-type': 'application/json'}
    function (error, response) {
      if (error) {
        me.log('STATUS: ' + response.statusCode);
        return next(error);
      return next();

We can now add our newly created plugin to Homebridge by installing it globally:

npm install -g switch-plugin

Open the config.json file located in your Homebridge directory in your favorite text editor. In the accessory section, add info to the array:

  "accessory": "MyAwesomeSwitch",
  "getUrl": "",
  "postUrl": ""

Restart Homebridge and you shall now be able to switch on and off this fake switch through Home app on your iOS device.

You liked this article? You'd probably be a good match for our ever-growing tech team at Theodo.

Join Us

  • Brek7

    Fantastic post. I appreciate the granularity and explanation as I am a new explorer in the realm of IoT and the languages you utilize.

  • Lescai Ionel

    Running homebridge says that no plugin was loaded, even though my config.json looks ok and I did npm install switch-plugin. Am I missing something ?

  • Frédéric Barthelet

    Hi Lescai, most probably homebridge is not using the config.json you wrote. Your config.json file MUST be inside of .homebridge, which is inside of your home folder. On macOS and Linux, the full path for your config.json would be ~/.homebridge/config.json. Any error messages will contain the exact path where your config is expected to be found.

  • Timothy McJak

    First, thank you — excellent material.

    Second, could you be a little more explicit as to where some of this javascript gets added? For example, you say, “First, we need to inject our plugin within homebridge” and go on to show some javascript related to “mySwitch” … where does this live?

    I’m left still unclear as to the interplay between package.json, index.js, and the javascript you outline in the rest of the article.

    Again, thank you very much!

  • Frédéric Barthelet

    Hi Thimothy,
    Thank you for your interest :)
    All the javascript described in this article lives within the single “index.js” file. The package.json is only here to manage project dependancies. You can have a look at this exemple “index.js” from homebridge-smappee plugin in order to have a better overview of the whole code :

    Hope I made myself more understandable this time. Let me know if anything is still unclear 😉

  • Timur Manyanov

    Ok, here’s some troubleshooting for those who get errors when trying to flip the switch.

    If you get an error “TypeError: Cannot read property ‘statusCode’ of undefined”, then add:
    “if (statusCode)”
    before the line:
    “me.log(‘STATUS: ‘ + response.statusCode);”
    (in both cases)

    That will give you a real error this time:
    “Error setting Characteristic “On” to value 1: Argument error, options.body.”

    That’s an error from the “request” module, which you can fix by changing
    “body: { ‘targetState’: on }” to “body: JSON.stringify({ ‘targetState’: on })”

  • Gotham Varma

    Hi i’m new to this and i’m facing the following problem. Please try to take a look
    i’m attaching the logs

    [2018-5-17 03:23:19] Loaded plugin: homebridge-info
    [2018-5-17 03:23:19] Registering platform ‘homebridge-info.Info’
    [2018-5-17 03:23:19] —
    [2018-5-17 03:23:19] Loaded config.json with 1 accessories and 0 platforms.
    [2018-5-17 03:23:19] —
    [2018-5-17 03:23:19] Loading 1 accessories…
    throw new Error(“The requested accessory ‘” + name + “‘ was not registered by any plugin.”);

    Error: The requested accessory ‘MyAwesomeSwitch’ was not registered by any plugin.
    at API.accessory (C:UsersGultimateAppDataRoamingnpmnode_moduleshomebridgelibapi.js:64:13)
    at Server._loadAccessories (C:UsersGultimateAppDataRoamingnpmnode_moduleshomebridgelibserver.js:264:42)
    at (C:UsersGultimateAppDataRoamingnpmnode_moduleshomebridgelibserver.js:86:38)
    at module.exports (C:UsersGultimateAppDataRoamingnpmnode_moduleshomebridgelibcli.js:40:10)
    at Object. (C:UsersGultimateAppDataRoamingnpmnode_moduleshomebridgebinhomebridge:17:22)
    at Module._compile (module.js:652:30)
    at Object.Module._extensions..js (module.js:663:10)
    at Module.load (module.js:565:32)
    at tryModuleLoad (module.js:505:12)
    at Function.Module._load (module.js:497:3)

  • Rafael Trestini

    Apparently in some versions, the attribute “name” in accessory configuration (config.json) is required.

  • Joe

    I’m getting the following error:

    Error: Cannot find module ‘request’

    tried reinstalling almost everything from scratch — didn’t help

  • Alexandru Dragoescu


    How can I use persistent storage (keep accessory last state after reboot or service restart)?

  • kmfan03

    Hi all,
    thanks for the great HowTo. I think I got it, I tried some simple Fake Controls and it worked.
    So far, I was wondering what kind of “Service” could I use to achieve the following:
    I want to connect my inverter (photovoltaics) to HomeKit. For that, I programmed already a PHP script to request the Webserver of the inverter, therefore I can request this data: 1. current status (on/off/error), 2. the current power in WATT (e.g. 4.708 WATT), 3. the total power (inverter lifetime) already generated (e.g. 28.5 kilo Watt). There is a huge amount of Services provided by Homebridge/Homekit, like Fan, Faucet, Garagedoor, and so on. But al of them are specific. At least, a simple “String” Viewer would be enough. So I could put the inverter values as a String into it. But even such a simple Service I can not find.
    Any suggestions?

  • akr4

    Thank you for the article.

    It seems that you have to put some properties into package.json so that the plugin is registered. This is my example working fine.
    “name”: “homebridge-my-switch-plugin”,
    “version”: “1.0.0”,
    “main”: “index.js”,
    “keywords”: [ “homebridge-plugin” ],
    “engines”: {
    “homebridge”: “>=0.2.5”

  • Thom Patterson

    I’m thinking of building a plugin to use the ONVIF spec to control a PTZ camera. Specifically the camera may have up to 50 presets. I want to be able to use Homekit to move the camera to a preset. My use case is when a motion sensor is triggered, homekit would move the camera to that preset. Any recommendation for what “Service” I could piggy-back on to capture all of those preset values? It looks like “Stateless Programmable Switch” only has 3 values

    Characteristic.ProgrammableSwitchEvent.SINGLE_PRESS = 0;
    Characteristic.ProgrammableSwitchEvent.DOUBLE_PRESS = 1;
    Characteristic.ProgrammableSwitchEvent.LONG_PRESS = 2;