Skip to content

Instantly share code, notes, and snippets.

@knalli
Last active October 4, 2015 15:55
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save knalli/201eea71130136db2ca0 to your computer and use it in GitHub Desktop.
Save knalli/201eea71130136db2ca0 to your computer and use it in GitHub Desktop.
'use strict';
let Service = require("HAP-NodeJS").Service;
let Characteristic = require("HAP-NodeJS").Characteristic;
let WebSocketClient = require('websocket').client;
class Utils {
assertUtf8Message(message) {
return new Promise((resolve, reject) => {
if (message.type === 'utf8') {
resolve(message);
} else {
reject(message);
}
});
}
convertToJson(message) {
return new Promise((resolve, reject) => {
try {
resolve(JSON.parse(message.utf8Data));
} catch (e) {
reject(e);
}
});
}
isMessageOfTypeValues(json) {
let result = false;
if (Array.isArray(json)) {
if (json.length === 0) {
result = true;
} else if (typeof json[0].type !== 'undefined' && typeof json[0].devices !== 'undefined' && typeof json[0].values !== 'undefined') {
result = true;
}
}
return result;
}
isMessageOfTypeUpdate(json) {
return (json.origin == 'update' && typeof json.type !== 'undefined' && typeof json.devices !== 'undefined' && typeof json.values !== 'undefined');
}
}
class PilightWebsocketAccessory {
constructor(log, config) {
this.log = log;
this.deviceStates = {};
this.config = {
host: config.host || 'localhost',
port: config.port || 5001,
deviceId: config.device
};
this.client = new WebSocketClient();
this.client.connect('ws://' + this.config.host + ':' + this.config.port + '/');
// FIXME: Should handle error
// FIXME: Should cache/multiplex identical connections?
this.client.on('connect', (connection) => {
this.log('Connection established: ' + 'ws://' + this.config.host + ':' + this.config.port + '/');
this._connection = connection;
this._connection.on('message', (message) => this.handleMessage(message));
// initial request all available values
this._connection.sendUTF(JSON.stringify({action: 'request values'}));
});
}
handleMessage(rawMessage) {
let utils = new Utils();
return Promise.resolve(rawMessage)
.then(utils.assertUtf8Message)
.then(utils.convertToJson)
.then((json) => {
if (utils.isMessageOfTypeValues(json)) {
// bulk update ("request values")
this.deviceStates = {};
for (let item of json) {
for (let device of item.devices) {
this.deviceStates[device] = item.values.state === 'on';
}
this.log('Updated internal states of devices: ' + item.devices);
}
} else if (utils.isMessageOfTypeUpdate(json)) {
// item update (after "control")
for (let device of json.devices) {
this.deviceStates[device] = json.values.state === 'on';
}
this.log('Updated internal states of devices: ' + json.devices);
}
})
.catch(() => {
this.log('Something went wrong, cannot parse message');
});
}
setPowerState(powerOn, callback) {
// FIXME: Should handle error somwhow!
this._connection.sendUTF(JSON.stringify({action: "control", code: {device: this.config.deviceId, state: (powerOn ? 'on' : 'off')}}));
callback();
}
getPowerState(callback) {
if (!this.deviceStates || typeof this.deviceStates[this.config.deviceId] === 'undefined') {
this.log('No power state found for device ' + this.config.deviceId);
callback(new Error("Not found"));
} else {
callback(null, this.deviceStates[this.config.deviceId]);
}
}
identify(callback) {
this.log("Identify requested!");
callback(); // success
}
getServices() {
// you can OPTIONALLY create an information service if you wish to override
// the default values for things like serial number, model, etc.
let informationService = new Service.AccessoryInformation();
informationService
.setCharacteristic(Characteristic.Manufacturer, "Pilight Manufacturer")
.setCharacteristic(Characteristic.Model, "Pilight Model")
.setCharacteristic(Characteristic.SerialNumber, "Pilight Serial Number");
let lightbulbService = new Service.Lightbulb();
lightbulbService
.getCharacteristic(Characteristic.On)
.on('set', this.setPowerState.bind(this));
lightbulbService
.getCharacteristic(Characteristic.On)
.on('get', this.getPowerState.bind(this));
return [informationService, lightbulbService];
}
}
module.exports = {
accessory: PilightWebsocketAccessory
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment