UNPKG

node-red-contrib-homekit-abc

Version:

HAP-Nodejs based Node-RED nodes to create HomeKit Accessories

625 lines (595 loc) 27.2 kB
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); };