node-red-contrib-homekit-abc
Version:
HAP-Nodejs based Node-RED nodes to create HomeKit Accessories
625 lines (595 loc) • 27.2 kB
JavaScript
const miio = require('miio');
const path = require('path');
const net = require('net');
const hap = require('hap-nodejs');
const storage = require('node-persist');
const {uuid, Accessory, Service, Characteristic} = hap;
const pkg = require('../package.json');
const fs = require('fs');
const ColourHelper = require('./ColourHelper');
const accessories = {};
module.exports = function (RED) {
//hap.init(path.join(RED.settings.userDir, 'homekit'));
RED.httpAdmin.get('/hassbian-abc-homekit-yeelight', (req, res) => {
if (req.query.config) {
const acc = accessories[req.query.config];
if (acc) {
res.status(200).send(JSON.stringify({setupURI: acc.setupURI()}));
} else {
res.status(500).send(JSON.stringify({}));
}
} else {
res.status(404).send(JSON.stringify({}));
}
});
class hassbianHomeKityeelight {
constructor(config) {
RED.nodes.createNode(this, config);
const that = this;
this.device = new miio.Device({
address: config.ip,
token: config.token
});
this.model = config.model
function logger(...args) {
let str = args.join(' ');
if (str.match(/^(error: )/i)) {
str = str.replace(/^error: (.*)/i, '$1');
that.error(str);
} else if (config.debug) {
that.debug(str);
}
}
if (accessories[this.id]) {
this.debug('yeelight already configured ' + this.name + ' ' + this.id + ' ' + this.username);
return;
}
const [firstLogger] = Object.keys(RED.settings.logging);
config.debug = config.debug && (RED.settings.logging[firstLogger].level === 'debug' || RED.settings.logging[firstLogger].level === 'trace');
this.name = config.name || ('yeelight ' + this.id);
storage.initSync();
const acc = new Accessory(this.name, uuid.generate(config.id), Accessory.Categories.LIGHTBULB);
accessories[this.id] = acc;
this.debug('yeelight created' + this.name + ' ' + this.id + ' ' + config.username);
acc.getService(Service.AccessoryInformation)
.setCharacteristic(Characteristic.Manufacturer, 'hassbian-abc')
.setCharacteristic(Characteristic.Model, 'yeelight')
.setCharacteristic(Characteristic.SerialNumber, config.username)
.setCharacteristic(Characteristic.FirmwareRevision, pkg.version);
acc.on('identify', (paired, callback) => {
this.log('identify ' + this.id + ' ' + this.username + ' ' + paired);
callback();
});
if (config.model === "CeilingLamp") {
var YeCeilingLampServices = acc.addService(Service.Lightbulb, this.name, this.name);
var CeilingLampOnCharacteristic = YeCeilingLampServices.getCharacteristic(Characteristic.On);
YeCeilingLampServices
.addCharacteristic(Characteristic.ColorTemperature)
.setProps({
minValue: 50,
maxValue: 400,
minStep: 1
});
CeilingLampOnCharacteristic
.on('get', function(callback) {
this.device.call("get_prop", ["power"]).then(result => {
that.debug("[ReYeelight][DEBUG]CeilingLamp - getPower: " + result);
callback(null, result[0] === 'on' ? true : false);
}).catch(function(err) {
that.debug("[ReYeelight][ERROR]CeilingLamp - getPower Error: " + err);
callback(err);
});
}.bind(this))
.on('set', function(value, callback) {
that.device.call("set_power", [value ? "on" : "off"]).then(result => {
that.debug("[ReYeelight][DEBUG]CeilingLamp - setPower Result: " + result);
if(result[0] === "ok") {
callback(null);
} else {
callback(new Error(result[0]));
}
}).catch(function(err) {
that.debug("[ReYeelight][ERROR]CeilingLamp - setPower Error: " + err);
callback(err);
});
}.bind(this));
YeCeilingLampServices
.addCharacteristic(Characteristic.Brightness)
.on('get', function(callback) {
this.device.call("get_prop", ["bright"]).then(result => {
that.debug("[ReYeelight][DEBUG]CeilingLamp - getBrightness: " + result);
callback(null, result[0]);
}).catch(function(err) {
that.debug("[ReYeelight][ERROR]CeilingLamp - getBrightness Error: " + err);
callback(err);
});
}.bind(this))
.on('set', function(value, callback) {
if(value > 0) {
this.device.call("set_bright", [value]).then(result => {
that.debug("[ReYeelight][DEBUG]CeilingLamp - setBrightness Result: " + result);
if(result[0] === "ok") {
callback(null);
} else {
callback(new Error(result[0]));
}
}).catch(function(err) {
that.debug("[ReYeelight][ERROR]CeilingLamp - setBrightness Error: " + err);
callback(err);
});
} else {
callback(null);
}
}.bind(this));
YeCeilingLampServices
.getCharacteristic(Characteristic.ColorTemperature)
.on('get', function(callback) {
this.device.call("get_prop", ["ct"]).then(result => {
ct = result[0] - 2700;
ct = ct / 3800 * 100;
ct = 100 - ct;
that.debug("[ReYeelight][DEBUG]CeilingLamp - getColorTemperature: " + ct + "%");
callback(null, ct);
}).catch(function(err) {
that.debug("[ReYeelight][ERROR]CeilingLamp - getColorTemperature Error: " + err);
callback(err);
});
}.bind(this))
.on('set', function(value,callback) {
value = value - 50;
value = value / 350 * 100;
value = Math.round(100 - value);
if(value == 0) {
value = 1;
}
ct = 3800 * value / 100;
ct = ct + 2700;
that.debug("[ReYeelight]CeilingLamp - setColorTemperature : " + ct);
this.device.call("set_ct_abx", [ct,"smooth",500]).then(result => {
that.debug("[ReYeelight][DEBUG]CeilingLamp - setColorTemperature Result: " + result);
if(result[0] === "ok") {
callback(null);
} else {
callback(new Error(result[0]));
}
}).catch(function(err) {
that.debug("[ReYeelight][ERROR]CeilingLamp - setColorTemperature Error: " + err);
callback(err);
});
}.bind(this));
} else if (config.model === "ColorLEDBulb") {
this.sat = 100;
this.hue = 360;
this.ct = 100;
this.ColourHelper = new ColourHelper();
var ColorLEDBulbService = acc.addService(Service.Lightbulb, this.name, this.name);
var ColorLEDBulbOnCharacteristic = ColorLEDBulbService.getCharacteristic(Characteristic.On);
ColorLEDBulbOnCharacteristic
.on('get', function(callback) {
this.device.call("get_prop", ["power"]).then(result => {
that.debug("[ReYeelight][DEBUG]ColorLEDBulb - getPower: " + result);
callback(null, result[0] === 'on' ? true : false);
}).catch(function(err) {
that.debug("[ReYeelight][ERROR]ColorLEDBulb - getPower Error: " + err);
callback(err);
});
}.bind(this))
.on('set', function(value, callback) {
that.device.call("set_power", [value ? "on" : "off"]).then(result => {
that.debug("[ReYeelight][DEBUG]ColorLEDBulb - setPower Result: " + result);
if(result[0] === "ok") {
callback(null);
} else {
callback(new Error(result[0]));
}
}).catch(function(err) {
that.debug("[ReYeelight][ERROR]ColorLEDBulb - setPower Error: " + err);
callback(err);
});
}.bind(this));
ColorLEDBulbService
.addCharacteristic(Characteristic.Hue)
.setProps({
minValue: 0,
maxValue: 360,
minStep: 1
});
ColorLEDBulbService
.addCharacteristic(Characteristic.Brightness)
.on('get', function(callback) {
this.device.call("get_prop", ["bright"]).then(result => {
that.debug("[ReYeelight][DEBUG]ColorLEDBulb - getBrightness: " + result);
callback(null, result[0]);
}).catch(function(err) {
that.debug("[ReYeelight][ERROR]ColorLEDBulb - getBrightness Error: " + err);
callback(err);
});
}.bind(this))
.on('set', function(value, callback) {
if(value > 0) {
this.device.call("set_bright", [value]).then(result => {
that.debug("[ReYeelight][DEBUG]ColorLEDBulb - setBrightness Result: " + result);
if(result[0] === "ok") {
callback(null);
} else {
callback(new Error(result[0]));
}
}).catch(function(err) {
that.debug("[ReYeelight][ERROR]ColorLEDBulb - setBrightness Error: " + err);
callback(err);
});
} else {
callback(null);
}
}.bind(this));
ColorLEDBulbService
.getCharacteristic(Characteristic.Hue)
.on('get', function(callback) {
this.device.call("get_prop", ["rgb"]).then(result => {
that.debug("[ReYeelight][DEBUG]ColorLEDBulb - getRGB: " + result);
rgb = this.ColourHelper.argb2rgb(result[0]);
hsb = this.ColourHelper.rgb2hsb(rgb);
this.hue = hsb[0];
callback(null, hsb[0]);
}).catch(function(err) {
that.debug("[ReYeelight][ERROR]ColorLEDBulb - getRGB Error: " + err);
callback(err);
});
}.bind(this))
.on('set', function(value, callback) {
this.hue = value;
rgb = this.ColourHelper.hsv2argb(parseFloat(value/360), parseFloat(this.sat/100), parseFloat(this.ct/100));
that.debug("[ReYeelight][ERROR]ColorLEDBulb - setRGB " + rgb + " From " + value);
this.device.call("set_rgb", [rgb]).then(result => {
that.debug("[ReYeelight][DEBUG]ColorLEDBulb - setRGBResult: " + result);
if(result[0] === "ok") {
callback(null);
} else {
callback(new Error(result[0]));
}
}).catch(function(err) {
that.debug("[ReYeelight][ERROR]ColorLEDBulb - setRGB Error: " + err);
callback(err);
});
}.bind(this));
ColorLEDBulbService
.addCharacteristic(Characteristic.Saturation)
.on('get', function(callback) {
that.debug("[ReYeelight][DEBUG]ColorLEDBulb - getSAT: " + this.sat);
this.device.call("get_prop", ["sat"]).then(result => {
that.debug("[ReYeelight][DEBUG]ColorLEDBulb - getSaturation: " + result);
this.sat = result[0];
callback(null, result[0]);
}).catch(function(err) {
that.debug("[ReYeelight][ERROR]ColorLEDBulb - getSaturation Error: " + err);
callback(err);
});
}.bind(this))
.on('set', function(value, callback) {
this.sat = value;
rgb = this.ColourHelper.hsv2argb(parseFloat(this.hue/360), parseFloat(value/100), parseFloat(this.ct/100));
that.debug("[ReYeelight][DEBUG]ColorLEDBulb - setSaturation " + rgb + " From Saturation " + value);
that.debug("[ReYeelight][DEBUG]ColorLEDBulb - setSAT: " + this.sat);
this.device.call("set_rgb", [rgb]).then(result => {
that.debug("[ReYeelight][DEBUG]ColorLEDBulb - setSaturationResult: " + result);
if(result[0] === "ok") {
callback(null);
} else {
callback(new Error(result[0]));
}
}).catch(function(err) {
that.debug("[ReYeelight][ERROR]ColorLEDBulb - setSaturation Error: " + err);
callback(err);
});
}.bind(this));
} else if (config.model === "ColorLEDStrip") {
this.sat = 100;
this.hue = 360;
this.ct = 100;
this.ColourHelper = new ColourHelper();
var ColorLEDStripService = acc.addService(Service.Lightbulb, this.name, this.name);
var ColorLEDStripOnCharacteristic = ColorLEDStripService.getCharacteristic(Characteristic.On);
ColorLEDStripOnCharacteristic
.on('get', function(callback) {
this.device.call("get_prop", ["power"]).then(result => {
that.debug("[ReYeelight][DEBUG]ColorLEDStrip - getPower: " + result);
callback(null, result[0] === 'on' ? true : false);
}).catch(function(err) {
that.debug("[ReYeelight][ERROR]ColorLEDStrip - getPower Error: " + err);
callback(err);
});
}.bind(this))
.on('set', function(value, callback) {
that.device.call("set_power", [value ? "on" : "off"]).then(result => {
that.debug("[ReYeelight][DEBUG]ColorLEDStrip - setPower Result: " + result);
if(result[0] === "ok") {
callback(null);
} else {
callback(new Error(result[0]));
}
}).catch(function(err) {
that.debug("[ReYeelight][ERROR]ColorLEDStrip - setPower Error: " + err);
callback(err);
});
}.bind(this));
ColorLEDStripService
.addCharacteristic(Characteristic.Hue)
.setProps({
minValue: 0,
maxValue: 360,
minStep: 1
});
ColorLEDStripService
.addCharacteristic(Characteristic.Brightness)
.on('get', function(callback) {
this.device.call("get_prop", ["bright"]).then(result => {
that.debug("[ReYeelight][DEBUG]ColorLEDStrip - getBrightness: " + result);
callback(null, result[0]);
}).catch(function(err) {
that.debug("[ReYeelight][ERROR]ColorLEDStrip - getBrightness Error: " + err);
callback(err);
});
}.bind(this))
.on('set', function(value, callback) {
if(value > 0) {
this.device.call("set_bright", [value]).then(result => {
that.debug("[ReYeelight][DEBUG]ColorLEDStrip - setBrightness Result: " + result);
if(result[0] === "ok") {
callback(null);
} else {
callback(new Error(result[0]));
}
}).catch(function(err) {
that.debug("[ReYeelight][ERROR]ColorLEDStrip - setBrightness Error: " + err);
callback(err);
});
} else {
callback(null);
}
}.bind(this));
ColorLEDStripService
.getCharacteristic(Characteristic.Hue)
.on('get', function(callback) {
this.device.call("get_prop", ["rgb"]).then(result => {
that.debug("[ReYeelight][DEBUG]ColorLEDStrip - getRGB: " + result);
rgb = this.ColourHelper.argb2rgb(result[0]);
hsb = this.ColourHelper.rgb2hsb(rgb);
this.hue = hsb[0];
callback(null, hsb[0]);
}).catch(function(err) {
that.debug("[ReYeelight][ERROR]ColorLEDStrip - getRGB Error: " + err);
callback(err);
});
}.bind(this))
.on('set', function(value, callback) {
this.hue = value;
rgb = this.ColourHelper.hsv2argb(parseFloat(value/360), parseFloat(this.sat/100), parseFloat(this.ct/100));
that.debug("[ReYeelight][ERROR]ColorLEDStrip - setRGB " + rgb + " From " + value);
this.device.call("set_rgb", [rgb]).then(result => {
that.debug("[ReYeelight][DEBUG]ColorLEDStrip - setRGBResult: " + result);
if(result[0] === "ok") {
callback(null);
} else {
callback(new Error(result[0]));
}
}).catch(function(err) {
that.debug("[ReYeelight][ERROR]ColorLEDStrip - setRGB Error: " + err);
callback(err);
});
}.bind(this));
ColorLEDStripService
.addCharacteristic(Characteristic.Saturation)
.on('get', function(callback) {
this.device.call("get_prop", ["sat"]).then(result => {
that.debug("[ReYeelight][DEBUG]ColorLEDStrip - getSaturation: " + result);
this.sat = result[0];
callback(null, result[0]);
}).catch(function(err) {
that.debug("[ReYeelight][ERROR]ColorLEDStrip - getSaturation Error: " + err);
callback(err);
});
}.bind(this))
.on('set', function(value, callback) {
this.sat = value;
rgb = this.ColourHelper.hsv2argb(parseFloat(this.hue/360), parseFloat(value/100), parseFloat(this.ct/100));
that.debug("[ReYeelight][ERROR]ColorLEDStrip - setSaturation " + rgb + " From Saturation " + value);
this.device.call("set_rgb", [rgb]).then(result => {
that.debug("[ReYeelight][DEBUG]ColorLEDStrip - setSaturationResult: " + result);
if(result[0] === "ok") {
callback(null);
} else {
callback(new Error(result[0]));
}
}).catch(function(err) {
that.debug("[ReYeelight][ERROR]ColorLEDStrip - setSaturation Error: " + err);
callback(err);
});
}.bind(this));
} else if (config.model === "DeskLamp") {
var YeDeskLampServices = acc.addService(Service.Lightbulb, this.name, this.name);
var DeskLampOnCharacteristic = YeDeskLampServices.getCharacteristic(Characteristic.On);
YeDeskLampServices
.addCharacteristic(Characteristic.ColorTemperature)
.setProps({
minValue: 50,
maxValue: 400,
minStep: 1
});
DeskLampOnCharacteristic
.on('get', function(callback) {
this.device.call("get_prop", ["power"]).then(result => {
that.debug("[ReYeelight][DEBUG]DeskLamp - getPower: " + result);
callback(null, result[0] === 'on' ? true : false);
}).catch(function(err) {
that.debug("[ReYeelight][ERROR]DeskLamp - getPower Error: " + err);
callback(err);
});
}.bind(this))
.on('set', function(value, callback) {
that.device.call("set_power", [value ? "on" : "off"]).then(result => {
that.debug("[ReYeelight][DEBUG]DeskLamp - setPower Result: " + result);
if(result[0] === "ok") {
callback(null);
} else {
callback(new Error(result[0]));
}
}).catch(function(err) {
that.debug("[ReYeelight][ERROR]DeskLamp - setPower Error: " + err);
callback(err);
});
}.bind(this));
YeDeskLampServices
.addCharacteristic(Characteristic.Brightness)
.on('get', function(callback) {
this.device.call("get_prop", ["bright"]).then(result => {
that.debug("[ReYeelight][DEBUG]DeskLamp - getBrightness: " + result);
callback(null, result[0]);
}).catch(function(err) {
that.debug("[ReYeelight][ERROR]DeskLamp - getBrightness Error: " + err);
callback(err);
});
}.bind(this))
.on('set', function(value, callback) {
if(value > 0) {
this.device.call("set_bright", [value]).then(result => {
that.debug("[ReYeelight][DEBUG]DeskLamp - setBrightness Result: " + result);
if(result[0] === "ok") {
callback(null);
} else {
callback(new Error(result[0]));
}
}).catch(function(err) {
that.debug("[ReYeelight][ERROR]DeskLamp - setBrightness Error: " + err);
callback(err);
});
} else {
callback(null);
}
}.bind(this));
YeDeskLampServices
.getCharacteristic(Characteristic.ColorTemperature)
.on('get', function(callback) {
this.device.call("get_prop", ["ct"]).then(result => {
ct = result[0] - 2700;
ct = ct / 3800 * 100;
ct = 100 - ct;
that.debug("[ReYeelight][DEBUG]DeskLamp - getColorTemperature: " + ct + "%");
callback(null, ct);
}).catch(function(err) {
that.debug("[ReYeelight][ERROR]DeskLamp - getColorTemperature Error: " + err);
callback(err);
});
}.bind(this))
.on('set', function(value,callback) {
value = value - 50;
value = value / 350 * 100;
value = Math.round(100 - value);
if(value == 0) {
value = 1;
}
ct = 3800 * value / 100;
ct = ct + 2700;
that.debug("[ReYeelight]DeskLamp - setColorTemperature : " + ct);
this.device.call("set_ct_abx", [ct,"smooth",500]).then(result => {
that.debug("[ReYeelight][DEBUG]DeskLamp - setColorTemperature Result: " + result);
if(result[0] === "ok") {
callback(null);
} else {
callback(new Error(result[0]));
}
}).catch(function(err) {
that.debug("[ReYeelight][ERROR]DeskLamp - setColorTemperature Error: " + err);
callback(err);
});
}.bind(this));
} else if (config.model === "WhiteBulb") {
var WhiteBulbService = acc.addService(Service.Lightbulb, this.name, this.name);
var WhiteBulbOnCharacteristic = WhiteBulbService.getCharacteristic(Characteristic.On);
WhiteBulbOnCharacteristic
.on('get', function(callback) {
this.device.call("get_prop", ["power"]).then(result => {
that.debug("[ReYeelight][DEBUG]WhiteBulb - getPower: " + result);
callback(null, result[0] === 'on' ? true : false);
}).catch(function(err) {
that.debug("[ReYeelight][ERROR]WhiteBulb - getPower Error: " + err);
callback(err);
});
}.bind(this))
.on('set', function(value, callback) {
that.device.call("set_power", [value ? "on" : "off"]).then(result => {
that.debug("[ReYeelight][DEBUG]WhiteBulb - setPower Result: " + result);
if(result[0] === "ok") {
callback(null);
} else {
callback(new Error(result[0]));
}
}).catch(function(err) {
that.debug("[ReYeelight][ERROR]WhiteBulb - setPower Error: " + err);
callback(err);
});
}.bind(this));
WhiteBulbService
.addCharacteristic(Characteristic.Brightness)
.on('get', function(callback) {
this.device.call("get_prop", ["bright"]).then(result => {
that.debug("[ReYeelight][DEBUG]WhiteBulb - getBrightness: " + result);
callback(null, result[0]);
}).catch(function(err) {
that.debug("[ReYeelight][ERROR]WhiteBulb - getBrightness Error: " + err);
callback(err);
});
}.bind(this))
.on('set', function(value, callback) {
if(value > 0) {
this.device.call("set_bright", [value]).then(result => {
that.debug("[ReYeelight][DEBUG]WhiteBulb - setBrightness Result: " + result);
if(result[0] === "ok") {
callback(null);
} else {
callback(new Error(result[0]));
}
}).catch(function(err) {
that.debug("[ReYeelight][ERROR]WhiteBulb - setBrightness Error: " + err);
callback(err);
});
} else {
callback(null);
}
}.bind(this));
}
this.log('publishing yeelight ' + this.name + ' ' + config.username);
const testPort = net.createServer()
.once('error', err => {
this.error(err);
this.status({fill: 'red', shape: 'dot', text: err.message});
})
.once('listening', () => {
testPort.once('close', () => {
acc.publish({
username: config.username,
port: config.port,
pincode: config.pincode,
category: Accessory.Categories.LIGHTBULB
});
acc._server.on('listening', () => {
this.log('yeelight ' + this.name + ' listening on port ' + config.port);
this.status({fill: 'green', shape: 'ring', text: ' '});
});
acc._server.on('pair', username => {
this.log('yeelight ' + this.name + ' paired', username);
});
acc._server.on('unpair', username => {
this.log('yeelight ' + this.name + ' unpaired', username);
});
acc._server.on('verify', () => {
this.log('yeelight ' + this.name + ' verify');
});
}).close();
})
.listen(config.port);
this.on('close', () => {
});
}
}
RED.nodes.registerType('hassbian-abc-homekit-yeelight', hassbianHomeKityeelight);
};