Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork627
Using HAP NodeJS as a library
Before reading this guide, you should already have read the wiki page aboutHomeKit Terminology.
In this guide, we are going to create a NodeJS module using HAP-NodeJS as a library to expose a simple HomeKit Light accessory.
It basically resembles the light example provided in theexamplesrepository. So feel free to have a look at that as well, especially if you want an example written in Typescript, as thisguide will only show an example written in JavaScript for the sake of a simpler project setup.
First, we need to set up our toolchain. For this, we will install Node.js and the Node Package Manager (npm)to manage all dependencies for our project. This guide does currently not provide any installation instructionsfor Node.js for every platform. A good starting point is to just navigate to theNode.js website.You may want to grab the Long-Term Support (LTS) version.
You may want to ensure that you install the minimum required node version required byHAP-NodeJS.
After that,node andnpm should be installed on your computer. You can verify that by running the following commands.A similar output should be printed.
$ node -vv13.13.0$ npm -v6.14.4To write our code, you can use an IDE like Visual Studio Code, WebStorm or just a simple text editor like Atom or Notepad++.
Now we want to create a new project by creating a new folder and opening your IDE in it. Some IDEs give prompts to createnew projects and will open it automatically. In that case make sure you create anempty node.js project.
You may additionally want to open your project folder in the terminal.
We will create two files for our project:package.json andlight.js.
Below are two sections where the complete content of each file is presented and then explained below line for line.So feel free to just copy the content, read through the explanations below and maybe follow additional instructions.
Thepackage.json is a file describing your npm module and must be present in every project.It defines stuff like the entry point in your app or dependencies. For more details visit theofficial documentation for thepackage.json file.
It is written inJavaScript Object Notation.
{"name":"your-project-name","version":"1.0.0","description":"An example description for your example project","main":"light.js","author":"Example Name <mail@example.com>","license":"ISC","dependencies": {"hap-nodejs":"latest" }}An explanation for the relevant fields used above:
name: this property sets the name of your module. It is used if you want to publish your package to thenpm registryversion: this property is used to define the current version and should useSemantic Versioningdescription: just a short descriptive text of your modulemain: this is the main entry point for your module. This is where execution begins. We set it here to thelight.jsfile we will create later.author: in this property you should put in your name or username to mark your ownershiplicense: define the license the project is published with. For our examples the homebridge team uses theISC license.dependencies: this is one of the more important properties. Here we define dependencies of other npm modules. For ourlight example we only have a dependency ofhap-nodejswith the versionlatest. Fell free to replace that with anyspecific version ofhap-nodejsif you require that.
After we created that you want to open some sort of terminal and run the following command in the same directory asthepackage.json. This will install any dependencies (or dependencies of dependencies) required by your module.
npm installlight.js contains all code need to create our example light accessory.
consthap=require("hap-nodejs");constAccessory=hap.Accessory;constCharacteristic=hap.Characteristic;constCharacteristicEventTypes=hap.CharacteristicEventTypes;constService=hap.Service;// optionally set a different storage location with code below// hap.HAPStorage.setCustomStoragePath("...");constaccessoryUuid=hap.uuid.generate("hap.examples.light");constaccessory=newAccessory("Example Accessory Name",accessoryUuid);constlightService=newService.Lightbulb("Example Lightbulb");letcurrentLightState=false;// on or offletcurrentBrightnessLevel=100;// 'On' characteristic is required for the light serviceconstonCharacteristic=lightService.getCharacteristic(Characteristic.On);// 'Brightness' characteristic is optional for the light service; 'getCharacteristic' will automatically add it to the service!constbrightnessCharacteristic=lightService.getCharacteristic(Characteristic.Brightness);// with the 'on' function we can add event handlers for different events, mainly the 'get' and 'set' eventonCharacteristic.on(CharacteristicEventTypes.GET,callback=>{console.log("Queried current light state: "+currentLightState);callback(undefined,currentLightState);});onCharacteristic.on(CharacteristicEventTypes.SET,(value,callback)=>{console.log("Setting light state to: "+value);currentLightState=value;callback();});brightnessCharacteristic.on(CharacteristicEventTypes.GET,(callback)=>{console.log("Queried current brightness level: "+currentBrightnessLevel);callback(undefined,currentBrightnessLevel);});brightnessCharacteristic.on(CharacteristicEventTypes.SET,(value,callback)=>{console.log("Setting brightness level to: "+value);currentBrightnessLevel=value;callback();});accessory.addService(lightService);// adding the service to the accessory// once everything is set up, we publish the accessory. Publish should always be the last step!accessory.publish({username:"17:51:07:F4:BC:8A",pincode:"678-90-876",port:47129,category:hap.Categories.LIGHTBULB,// value here defines the symbol shown in the pairing screen});console.log("Accessory setup finished!");
Before I explain the code in more detail you may want to just quickly test it and run it using the following command.The default pairing code is678-90-876.
node light.jsBelow we will discuss some parts of the code in more detail.
consthap=require("hap-nodejs");constAccessory=hap.Accessory;constCharacteristic=hap.Characteristic;constCharacteristicEventTypes=hap.CharacteristicEventTypes;constService=hap.Service;
The first few lines are all about importing theHAP-NodeJS library. It saves everything exported from the library intothe variable namedhap. The other variables are just there to make the code what follows cleaner. If we want to accessthe accessory class we do not need to always writehap.Accessory instead we will only writeAccessory. We could giveit a totally different name, but using the same name as defined byHAP-NodeJS improves code readability a lot.
constaccessoryUuid=hap.uuid.generate("hap.examples.light");constaccessory=newAccessory("Example Accessory Name",accessoryUuid);
This two lines create a new accessory object from theAccessory class (saved into theaccessory variable).The first argument passed is the name of the accessory displayed in the pairing screen, the second argument is the uuid.
One thing to point out is the first line, which generates aUniversally Unique Identifier.In this example it is derived from the generator data"hap-examples.light". The same UUID will be generated for thesame input. This UUID is used byHAP-NodeJS touniquely identify an accessory and its related data stored on disk.So it must not change over the lifespan of an accessory, otherwise your configuration of the services exposed by thisaccessory may be reset in the Home app (like room assignments, automations and scenes).
constlightService=newService.Lightbulb("Example Lightbulb");letcurrentLightState=false;// on or off
The first line creates a new service, to be specific aLightbulb service. The first argument is the name of the service(this is actually the name displayed in the tile in the Home app). All services supported byHAP-NodeJS can be accessedin that wayService.*.
A list of all services and characteristics can be found inServiceDefinitions.ts andCharacteristicDefinitions.ts respectively. Just open them andsearch forLighbulb and you will find the service definition of theLighbulb service with all its required andoptional characteristics, like theOn characteristic (or the optionalBrightness characteristic present in the code above).
We only cover theOn characteristic in the detailed explanation, but you easily should be able to understand the codeabove for theBrightness characteristic as well, as it is pretty similar.
The second line is just a variable (a variable whose value can be changed, thuslet and notconst) where we storethe current on/off state of the lightbulb.
constonCharacteristic=lightService.getCharacteristic(Characteristic.On);// with the 'on' function we can add event handlers for different events, mainly the 'get' and 'set' eventonCharacteristic.on(CharacteristicEventTypes.GET,callback=>{console.log("Queried current light state: "+currentLightState);callback(undefined,currentLightState);});onCharacteristic.on(CharacteristicEventTypes.SET,(value,callback)=>{console.log("Setting light state to: "+value);currentLightState=value;callback();});
This part is where the real magic is happening. First of all we are using calling thegetCharacteristic method ofourlightService object to get a reference to the characteristics object for theOn characteristic (Characteristic.On).
Using theon method we then subscribe twoevent listeners, one for the'get' event and one for the'set' event.
The'get' is called whenever a HomeKit controller request the current value of the characteristic. Acallback is passedto the event handler as the first argument. The callback expects an Error object as the first argument if an error occurred orundefined as the first argument and the current value as the second argument to return the current value. The callbackshould be called as soon as possible.
The'set' is called whenever a HomeKit controller sets a new value for the given characteristic. The newly setvalueis passed as the first argument. Acallback is passed to the event handler as the second argument.The callback expects an Error object as the first argument if an error occurred.
accessory.addService(lightService);// adding the service to the accessory
After setting up our service we add it to our accessory by calling theaddService method of theaccessory.
// once everything is set up, we publish the accessory. Publish should always be the last step!accessory.publish({username:"17:51:07:F4:BC:8A",pincode:"678-90-876",port:47129,category:hap.Categories.LIGHTBULB,// value here defines the symbol shown in the pairing screen});
After everything is set up, the last thing we will do is call thepublish method of theaccessory.The method takes aPublishInfo, basically an object full of options, as the first argument. The most importantoptions are highlighted here:
username: this value is basically a mac address and must be in that format. It is used as an identifier of the HAPserver by HomeKit. It can be any random mac address, but must be persistent over the lifespan of the accessory(asHAP-NodeJSeven uses this to identify all stored files associated to this HAP server).pincode: This is the eight digit pairing code. It must be specified in the exact same pattern as shown above.port: This specifies the port the HAP (http) server is running on. Leave it out (or specify a value of0) touse a random (free) port on start up.category: This defines the category of the HAP server.It basically is just used to display the icon in the pairing menu.
So we basically covered now all important parts of the code. Let's start our accessory with the following command:
node light.jsNow you can navigate into the Home App and add the accessory to your Home with the provided setup code.
Horray you managed to add your first custom HomeKit accessory!
To get you started with HAP-NodeJS development, try to modify the provided example.
As defined by the HomeKit Accessory Protocol (HAP) specification, theLightbulb service has threeadditional optional characteristics:Hue andSaturation, to provide coloring functionality, orColorTemperature, to change the temperature of a white bulb.
Try adding one of those characteristics and play around with it in the Home App.
Additionally, you can check out different types of services and their set of characteristics andexplore how they react in the Home App.