node-red-contrib-homekit-abc
Version:
HAP-Nodejs based Node-RED nodes to create HomeKit Accessories
205 lines (168 loc) • 8.4 kB
JavaScript
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);
};