UNPKG

node-red-contrib-homekit-abc

Version:

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

205 lines (168 loc) 8.4 kB
const mqtt = require("mqtt"); const path = require('path'); const storage = require('node-persist'); const net = require('net'); const hap = require('hap-nodejs'); const {uuid, Accessory, Service, Characteristic, Bridge} = hap; const pkg = require('../package.json'); const bridges = {}; module.exports = function (RED) { //hap.init(path.join(RED.settings.userDir, 'homekit')); RED.httpAdmin.get('/hassbian-abc-homekit-haier-airbox', (req, res) => { if (req.query.config) { const acc = bridges[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 hassbianHomeKitairbox { constructor(config) { RED.nodes.createNode(this, config); const that = this; var port = 56800; var host = config.ip; 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 (bridges[this.id]) { this.debug('haier-airbox 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 || ('haier-airbox ' + this.id); storage.initSync(); const acc = new Bridge(this.name, uuid.generate(config.id), Accessory.Categories.OUTLET); bridges[this.id] = acc; this.debug('haier-airbox created' + this.name + ' ' + this.id + ' ' + config.username); acc.getService(Service.AccessoryInformation) .setCharacteristic(Characteristic.Manufacturer, 'hassbian-abc') .setCharacteristic(Characteristic.Model, 'haier-airbox') .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 airboxservice = acc.addService(Service.AirQualitySensor, "空气质量", "空气质量"); var VOCDensityCharacteristic = airboxservice.getCharacteristic(Characteristic.VOCDensity); var airQualityCharacteristic = airboxservice.getCharacteristic(Characteristic.AirQuality); //acc.addBridgedAccessory(airboxAccessory); var airboxAccessory = new Accessory('温湿度', uuid.generate('hap-nodejs:accessories:温湿度')); var service2 = airboxAccessory.addService(Service.TemperatureSensor, "温度"); var currentTemperatureCharacteristic = service2.getCharacteristic(Characteristic.CurrentTemperature); var service3 = airboxAccessory.addService(Service.HumiditySensor, "湿度"); var currentRelativeHumidityCharacteristic = service3.getCharacteristic(Characteristic.CurrentRelativeHumidity); acc.addBridgedAccessory(airboxAccessory); VOCDensityCharacteristic .on('get', (callback) => { var client = new net.Socket(); client.connect(port, host, function() { this.log('Connect to: ' + host + ':' + port); }); client.on('data', function(data) { if (data.length == 109 && data[2] == 0x27 && data[3] == 0x15){ var temp = ((data[92] << 8 | data[93]) - 300) / 10 -4.5; var hum = (data[94] << 8 | data[95])/10; var voc = (data[98] << 8 | data[99])/1000; currentTemperatureCharacteristic.updateValue(temp); currentRelativeHumidityCharacteristic.updateValue(hum); callback(null, voc); if (voc < 0.600) { airQualityCharacteristic.updateValue(Characteristic.AirQuality.EXCELLENT); } else { airQualityCharacteristic.updateValue(Characteristic.AirQuality.POOR); } this.log("temp: " + temp, "hum: " + hum, "voc: " + voc); } setTimeout(function() { client.destroy(); },500) }); client.on('error', function (err) { //console.log('Error: ' + err); currentTemperatureCharacteristic.updateValue(new Error("Polling failed")); currentRelativeHumidityCharacteristic.updateValue(new Error("Polling failed")); airQualityCharacteristic.updateValue(new Error("Polling failed")); callback("no_response"); }); }); setInterval(function() { var client = new net.Socket(); client.connect(port, host, function() { console.log('Connect to: ' + host + ':' + port); }); client.on('data', function(data) { if (data.length == 109 && data[2] == 0x27 && data[3] == 0x15){ var temp = ((data[92] << 8 | data[93]) - 300) / 10 -4.5; var hum = (data[94] << 8 | data[95])/10; var voc = (data[98] << 8 | data[99])/1000; currentTemperatureCharacteristic.updateValue(temp); currentRelativeHumidityCharacteristic.updateValue(hum); VOCDensityCharacteristic.updateValue(voc); if (voc < 0.600) { airQualityCharacteristic.updateValue(Characteristic.AirQuality.EXCELLENT); } else { airQualityCharacteristic.updateValue(Characteristic.AirQuality.POOR); } console.log("temp: " + temp, "hum: " + hum, "voc: " + voc); } setTimeout(function() { client.destroy(); },500) }); client.on('error', function (err) { //console.log('Error: ' + err); currentTemperatureCharacteristic.updateValue(new Error("Polling failed")); currentRelativeHumidityCharacteristic.updateValue(new Error("Polling failed")); VOCDensityCharacteristic.updateValue(new Error("Polling failed")); airQualityCharacteristic.updateValue(new Error("Polling failed")); }); }, 10*1000); this.log('publishing haier-airbox ' + 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.SENSOR }); acc._server.on('listening', () => { this.log('haier-airbox ' + this.name + ' listening on port ' + config.port); this.status({fill: 'green', shape: 'ring', text: ' '}); }); acc._server.on('pair', username => { this.log('haier-airbox ' + this.name + ' paired', username); }); acc._server.on('unpair', username => { this.log('haier-airbox ' + this.name + ' unpaired', username); }); acc._server.on('verify', () => { this.log('haier-airbox ' + this.name + ' verify'); }); }).close(); }) .listen(config.port); this.on('close', () => { }); } } RED.nodes.registerType('hassbian-abc-homekit-haier-airbox', hassbianHomeKitairbox); };