UNPKG

node-red-contrib-homekit-abc

Version:

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

233 lines (194 loc) 9.66 kB
const mqtt = require("mqtt"); const path = require('path'); const net = require('net'); const storage = require('node-persist'); const hap = require('hap-nodejs'); const {uuid, Accessory, Service} = 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-zm1', (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 hassbianHomeKitzm1 { constructor(config) { RED.nodes.createNode(this, config); const that = this; var mqttHost = config.mqtthost; var mqttUsername = config.mqttname; var mqttPassword = config.mqttpass; var mac = config.zm1MAC; var mqttOptions = { clientId: 'mqttjs_' + Math.random().toString(16).substr(2, 8), username: mqttUsername, password: mqttPassword }; var client = mqtt.connect(mqttHost, mqttOptions); client.on('error', function () { this.log('Error event on MQTT'); }); var topicSensor = "device/zm1/" + mac + "/sensor"; var topicLightSet = "device/zm1/" + mac + "/set"; var topicLightGet = "device/zm1/" + mac + "/state"; try { client.subscribe(topicSensor); client.subscribe(topicLightGet); client.publish(topicLightSet, "{\"mac\":\"b0f893277c32\",\"brightness\":null}"); } catch(e) { }; 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('zm1 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 || ('zm1 ' + this.id); storage.initSync(); const acc = new Accessory(this.name, uuid.generate(config.id), Accessory.Categories.OUTLET); accessories[this.id] = acc; this.debug('zm1 created' + this.name + ' ' + this.id + ' ' + config.username); acc.getService(Service.AccessoryInformation) .setCharacteristic(hap.Characteristic.Manufacturer, 'hassbian-abc') .setCharacteristic(hap.Characteristic.Model, 'zm1') .setCharacteristic(hap.Characteristic.SerialNumber, config.username) .setCharacteristic(hap.Characteristic.FirmwareRevision, pkg.version); acc.on('identify', (paired, callback) => { this.log('identify ' + this.id + ' ' + this.username + ' ' + paired); callback(); }); var lightService = acc.addService(hap.Service.Lightbulb, this.name + "屏幕", this.name + "屏幕"); var tempService = acc.addService(hap.Service.TemperatureSensor, "温度", "温度"); var humiditySensorService = acc.addService(hap.Service.HumiditySensor, "湿度", "湿度"); var airQualitySensorService = acc.addService(hap.Service.AirQualitySensor, "空气质量", this.name + "空气质量"); var onCharacteristic = lightService.getCharacteristic(hap.Characteristic.On); var brightnessCharacteristic = lightService.addCharacteristic(hap.Characteristic.Brightness); var currentTemperatureCharacteristic = tempService.getCharacteristic(hap.Characteristic.CurrentTemperature); var currentRelativeHumidityCharacteristic = humiditySensorService.getCharacteristic(hap.Characteristic.CurrentRelativeHumidity); airQualitySensorService.addOptionalCharacteristic(hap.Characteristic.AirParticulateDensity); airQualitySensorService.addOptionalCharacteristic(hap.Characteristic.AirParticulateSize); var airQualityCharacteristic = airQualitySensorService.getCharacteristic(hap.Characteristic.AirQuality); var pm2_5DensityCharacteristic = airQualitySensorService.getCharacteristic(hap.Characteristic.PM2_5Density); var VOCDensityCharacteristic = airQualitySensorService.getCharacteristic(hap.Characteristic.VOCDensity); var airParticulateSizeCharacteristic = airQualitySensorService.addCharacteristic(hap.Characteristic.AirParticulateSize); var airParticulateDensityCharacteristic = airQualitySensorService.addCharacteristic(hap.Characteristic.AirParticulateDensity); airParticulateSizeCharacteristic.updateValue(hap.Characteristic.AirParticulateSize._2_5_M); onCharacteristic.on('set', (value, callback) => { if (value == true) { if (brightnessCharacteristic.value !== 0) { var value1 = brightnessCharacteristic.value; } else { var value1 = 100; } var valueStr = value1.toString(); var valueStr1 = "{" + "\"mac\":" + "\"" + mac + "\"" + "," + "\"brightness\"" + ":" + valueStr + "}"; client.publish(topicLightSet, valueStr1); callback(); } else { client.publish(topicLightSet, "{\"mac\":\"b0f893277c32\",\"brightness\":0}"); callback(); } }); brightnessCharacteristic .setProps({ minValue: 0, maxValue: 4, minStep: 1, }) .on('set', (value, callback) => { var valueStr = value.toString(); var valueStr1 = "{" + "\"mac\":" + "\"" + mac + "\"" + "," + "\"brightness\"" + ":" + valueStr + "}"; client.publish(topicLightSet, valueStr1); callback(); }); client.on('message', (topic, message) => { if(topic == topicLightGet) { var a1 = JSON.parse(message); var a2 = a1.brightness; if (a2 == 0) { onCharacteristic.updateValue(false); } else if (a2 !== 0) { onCharacteristic.updateValue(true); }; brightnessCharacteristic.updateValue(a2); } else if (topic == topicSensor) { var b1 = JSON.parse(message); var b2 = b1.temperature; var b3 = b1.humidity; var b4 = b1.formaldehyde; var b5 = b1.PM25; pm2_5DensityCharacteristic.updateValue(b5); airParticulateDensityCharacteristic.updateValue(b5); currentRelativeHumidityCharacteristic.updateValue(b3); currentTemperatureCharacteristic.updateValue(b2); VOCDensityCharacteristic.updateValue(b4 * 1000); if (b5 <= 50) { airQualityCharacteristic.updateValue(hap.Characteristic.AirQuality.EXCELLENT); } else if(b5 > 50 && b5 <= 100) { airQualityCharacteristic.updateValue(hap.Characteristic.AirQuality.GOOD); } else if(b5 > 100 && b5 <= 200) { airQualityCharacteristic.updateValue(hap.Characteristic.AirQuality.FAIR); } else if(b5 > 200 && b5 <= 300) { airQualityCharacteristic.updateValue(hap.Characteristic.AirQuality.INFERIOR); } else if (b5 > 300) { airQualityCharacteristic.updateValue(hap.Characteristic.AirQuality.POOR); } else { airQualityCharacteristic.updateValue(hap.Characteristic.AirQuality.UNKNOWN); }; } }); this.log('publishing zm1 ' + 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('zm1 ' + this.name + ' listening on port ' + config.port); this.status({fill: 'green', shape: 'ring', text: ' '}); }); acc._server.on('pair', username => { this.log('zm1 ' + this.name + ' paired', username); }); acc._server.on('unpair', username => { this.log('zm1 ' + this.name + ' unpaired', username); }); acc._server.on('verify', () => { this.log('zm1 ' + this.name + ' verify'); }); }).close(); }) .listen(config.port); this.on('close', () => { }); } } RED.nodes.registerType('hassbian-abc-homekit-zm1', hassbianHomeKitzm1); };