UNPKG

node-red-contrib-homekit-abc

Version:

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

422 lines (382 loc) 17.7 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 accessories = {}; module.exports = function (RED) { //hap.init(path.join(RED.settings.userDir, 'homekit')); RED.httpAdmin.get('/hassbian-abc-homekit-mihumidifier', (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 hassbianHomeKitmihumidifier { 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('mihumidifier 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 || ('mihumidifier ' + this.id); storage.initSync(); const acc = new Accessory(this.name, uuid.generate(config.id), Accessory.Categories.AIR_HUMIDIFIER); accessories[this.id] = acc; this.debug('mihumidifier created' + this.name + ' ' + this.id + ' ' + config.username); acc.getService(Service.AccessoryInformation) .setCharacteristic(Characteristic.Manufacturer, 'hassbian-abc') .setCharacteristic(Characteristic.Model, 'mihumidifier') .setCharacteristic(Characteristic.SerialNumber, config.username) .setCharacteristic(Characteristic.FirmwareRevision, pkg.version); acc.on('identify', (paired, callback) => { this.log('identify ' + this.id + ' ' + this.username + ' ' + paired); callback(); }); var humidifierService = acc.addService(Service.HumidifierDehumidifier, this.name, this.name); var currentHumidityCharacteristic = humidifierService.getCharacteristic(Characteristic.CurrentRelativeHumidity); var currentHumidifierDehumidifierStateCharacteristic = humidifierService.getCharacteristic(Characteristic.CurrentHumidifierDehumidifierState); currentHumidifierDehumidifierStateCharacteristic.setProps({ validValues: [0,2] }); var targetHumidifierDehumidifierStateCharacteristic = humidifierService.getCharacteristic(Characteristic.TargetHumidifierDehumidifierState); targetHumidifierDehumidifierStateCharacteristic.setProps({ validValues: [1] }); var activeCharacteristic = humidifierService.getCharacteristic(Characteristic.Active); var lockPhysicalControlsCharacteristic = humidifierService.addCharacteristic(Characteristic.LockPhysicalControls); if (this.model === 'ca1' || 'cb1') { var waterLevel = humidifierService.getCharacteristic(Characteristic.WaterLevel); } var rotationSpeedCharacteristic = humidifierService.getCharacteristic(Characteristic.RotationSpeed); if (this.model === 'ca1' || 'cb1') { rotationSpeedCharacteristic.setProps({ minValue: 0, maxValue: 100, minStep: 25, }); } else { rotationSpeedCharacteristic.setProps({ minValue: 0, maxValue: 3, minStep: 1, }); } var swingModeControlsCharacteristic = humidifierService.addCharacteristic(Characteristic.SwingMode); humidifierService .getCharacteristic(Characteristic.RelativeHumidityHumidifierThreshold) .setProps({ minValue: 0, maxValue: 100, minStep: 10, }) .on('get', function(callback) { that.device.call("get_prop", ["power", "humidity", "child_lock", "dry", "depth", "limit_hum"]).then(result => { that.debug("[MiHumidifier][DEBUG] - get: " + result); if (result[0] === "on") { activeCharacteristic.updateValue(Characteristic.Active.ACTIVE); currentHumidifierDehumidifierStateCharacteristic.updateValue(Characteristic.CurrentHumidifierDehumidifierState.HUMIDIFYING); } else if (result[0] === "off") { activeCharacteristic.updateValue(Characteristic.Active.INACTIVE); currentHumidifierDehumidifierStateCharacteristic.updateValue(Characteristic.CurrentHumidifierDehumidifierState.INACTIVE); } if (result[2] === "on") { lockPhysicalControlsCharacteristic.updateValue(Characteristic.LockPhysicalControls.CONTROL_LOCK_ENABLED); } else if (result[2] === "off") { lockPhysicalControlsCharacteristic.updateValue(Characteristic.LockPhysicalControls.CONTROL_LOCK_DISABLED); } if (result[3] === "on") { swingModeControlsCharacteristic.updateValue(Characteristic.SwingMode.SWING_ENABLED); } else if (result[3] === "off") { swingModeControlsCharacteristic.updateValue(Characteristic.SwingMode.SWING_DISABLED); } if (this.model === 'ca1' || 'cb1') { waterLevel.updateValue(result[4] / 1.2); } currentHumidityCharacteristic.updateValue(result[1]); callback(null, result[5]) }).catch(function(err) { that.log("[MiHumidifier][DEBUG] - get Error: " + err); callback(err); }); }.bind(this)) .on('set', function(value, callback) { if(value > 0 && value <= 40) { value = 40; } else if(value > 80 && value <= 100) { value = 80; } that.device.call("set_limit_hum", [value]).then(result => { if(result[0] === "ok") { callback(null); } else { callback(new Error(result[0])); } }).catch(function(err) { callback(err); }); }.bind(this)); activeCharacteristic .on('set', function(value, callback) { that.device.call("set_power", [value ? "on" : "off"]).then(result => { if(result[0] === "ok") { callback(null); } else { callback(new Error(result[0])); } }).catch(function(err) { callback(err); }); }.bind(this)); targetHumidifierDehumidifierStateCharacteristic.setValue(Characteristic.TargetHumidifierDehumidifierState.HUMIDIFIER); swingModeControlsCharacteristic .on('set', function(value, callback) { that.device.call("set_dry", [value ? "on" : "off"]).then(result => { if(result[0] === "ok") { callback(null); } else { callback(new Error(result[0])); } }).catch(function(err) { callback(err); }); }.bind(this)); lockPhysicalControlsCharacteristic .on('set', function(value, callback) { that.debug("[MiHumidifier][DEBUG]HumidifierDehumidifier - Child Lock - setchildlock: " + value); that.device.call("set_child_lock", [value ? "on" : "off"]).then(result => { if(result[0] === "ok") { callback(null); } else { callback(new Error(result[0])); } }).catch(function(err) { that.log.debug("[MiHumidifier][DEBUG]HumidifierDehumidifier - Child Lock - setchildlock: " + err); callback(err); }); }.bind(this)); rotationSpeedCharacteristic .on('get', function(callback) { that.device.call('get_prop',['mode']).then(result => { that.debug("[MiHumidifier][DEBUG]HumidifierDehumidifier - getMode: " + result); if (this.model === 'ca1' || 'cb1') { if(result[0] === "auto") { callback(null, 25); } else if(result[0] === "silent") { callback(null, 50); } else if(result[0] === "medium") { callback(null, 75); } else if(result[0] === "high") { callback(null, 100); } else { callback(null, 0); } } else { if(result[0] === "silent") { callback(null, 1); } else if(result[0] === "medium") { callback(null, 2); } else if(result[0] === "high") { callback(null, 3); } else { callback(null, 0); } } }).catch(function(err) { that.debug("[MiHumidifier][DEBUG]HumidifierDehumidifier - getMode Error: " + err); callback(err); }); }.bind(this)) .on('set', function(value, callback) { that.debug("[MiHumidifier][DEBUG]HumidifierDehumidifier - setMode: " + value); if (this.model === 'ca1' || 'cb1') { if(value == 25) { that.device.call("set_mode", ["auto"]).then(result => { if(result[0] === "ok") { callback(null); } else { callback(new Error(result[0])); } }).catch(function(err) { that.debug("[MiHumidifier][DEBUG]HumidifierDehumidifier - setMode Error: " + err); callback(err); }); } else if (value == 50) { that.device.call("set_mode", ["silent"]).then(result => { if(result[0] === "ok") { callback(null); } else { callback(new Error(result[0])); } }).catch(function(err) { that.debug("[MiHumidifier][DEBUG]HumidifierDehumidifier - setMode Error: " + err); callback(err); }); } else if (value == 75) { that.device.call("set_mode", ["medium"]).then(result => { if(result[0] === "ok") { callback(null); } else { callback(new Error(result[0])); } }).catch(function(err) { that.debug("[MiHumidifier][DEBUG]HumidifierDehumidifier - setMode Error: " + err); callback(err); }); } else if (value == 100) { that.device.call("set_mode", ["high"]).then(result => { if(result[0] === "ok") { callback(null); } else { callback(new Error(result[0])); } }).catch(function(err) { that.debug("[MiHumidifier][DEBUG]HumidifierDehumidifier - setMode Error: " + err); callback(err); }); } else if (value == 0) { that.device.call("set_power", ["off"]).then(result => { if(result[0] === "ok") { callback(null); } else { callback(new Error(result[0])); } }).catch(function(err) { that.debug("[MiHumidifier][DEBUG]HumidifierDehumidifier - setMode Error: " + err); callback(err); }); } } else { if (value == 1) { that.device.call("set_mode", ["silent"]).then(result => { if(result[0] === "ok") { callback(null); } else { callback(new Error(result[0])); } }).catch(function(err) { that.debug("[MiHumidifier][DEBUG]HumidifierDehumidifier - setMode Error: " + err); callback(err); }); } else if (value == 2) { that.device.call("set_mode", ["medium"]).then(result => { if(result[0] === "ok") { callback(null); } else { callback(new Error(result[0])); } }).catch(function(err) { that.debug("[MiHumidifier][DEBUG]HumidifierDehumidifier - setMode Error: " + err); callback(err); }); } else if (value == 3) { that.device.call("set_mode", ["high"]).then(result => { if(result[0] === "ok") { callback(null); } else { callback(new Error(result[0])); } }).catch(function(err) { that.debug("[MiHumidifier][DEBUG]HumidifierDehumidifier - setMode Error: " + err); callback(err); }); } else if (value == 0) { that.device.call("set_power", ["off"]).then(result => { if(result[0] === "ok") { callback(null); } else { callback(new Error(result[0])); } }).catch(function(err) { that.debug("[MiHumidifier][DEBUG]HumidifierDehumidifier - setMode Error: " + err); callback(err); }); } } }.bind(this)); if(config.showTemperature) { var temperatureSensorService = acc.addService(Service.TemperatureSensor, config.showTemperatureSensorName, config.showTemperatureSensorName); temperatureSensorService .getCharacteristic(Characteristic.CurrentTemperature) .on('get', function(callback) { if (this.model !== 'cb1') { that.device.call("get_prop", ["temp_dec"]).then(result => { that.debug("[MiHumidifier][DEBUG]HumidifierDehumidifier - getTemperature: " + result); callback(null, result[0] / 10); }).catch(function(err) { that.log.debug("[MiHumidifier][DEBUG]HumidifierDehumidifier - getTemperature Error: " + err); callback(err); }); } else { that.device.call("get_prop", ["temperature"]).then(result => { that.debug("[MiHumidifier][DEBUG]HumidifierDehumidifier - getTemperature: " + result); callback(null, result[0]); }).catch(function(err) { that.debug("[MiHumidifier][DEBUG]HumidifierDehumidifier - getTemperature Error: " + err); callback(err); }); } }.bind(this)); } this.log('publishing mihumidifier ' + 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.AIR_HUMIDIFIER }); acc._server.on('listening', () => { this.log('mihumidifier ' + this.name + ' listening on port ' + config.port); this.status({fill: 'green', shape: 'ring', text: ' '}); }); acc._server.on('pair', username => { this.log('mihumidifier ' + this.name + ' paired', username); }); acc._server.on('unpair', username => { this.log('mihumidifier ' + this.name + ' unpaired', username); }); acc._server.on('verify', () => { this.log('mihumidifier ' + this.name + ' verify'); }); }).close(); }) .listen(config.port); this.on('close', () => { }); } } RED.nodes.registerType('hassbian-abc-homekit-mihumidifier', hassbianHomeKitmihumidifier); };