node-red-contrib-homekit-abc
Version:
HAP-Nodejs based Node-RED nodes to create HomeKit Accessories
434 lines (396 loc) • 18.4 kB
JavaScript
const broadlink = require('./broadlink');
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 accessories = {};
module.exports = function (RED) {
//hap.init(path.join(RED.settings.userDir, 'homekit'));
RED.httpAdmin.get('/hassbian-abc-homekit-broadlink-mp1', (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 hassbianHomeKitbroadlink {
constructor(config) {
RED.nodes.createNode(this, config);
const that = this;
var broadlinkip = config.ip;
var broadlinkmac = config.mac;
var mac_buff = function(mac) {
var mb = new Buffer(6);
if (mac) {
var values = mac.split(':');
if (!values || values.length !== 6) {
throw new Error('Invalid MAC [' + mac + ']; should follow pattern ##:##:##:##:##:##');
}
for (var i = 0; i < values.length; ++i) {
var tmpByte = parseInt(values[i], 16);
mb.writeUInt8(tmpByte, i);
}
} else {
//this.log("MAC address emtpy, using IP: " + this.ip);
}
return mb;
};
var powered = false;
var powered1 = false;
var powered2 = false;
var powered3 = false;
var time = true;
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('broadlink-mp1 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 || ('broadlink-mp1 ' + this.id);
storage.initSync();
const acc = new Accessory(this.name, uuid.generate(config.id), Accessory.Categories.OUTLET);
accessories[this.id] = acc;
this.debug('broadlink-mp1 created' + this.name + ' ' + this.id + ' ' + config.username);
acc.getService(Service.AccessoryInformation)
.setCharacteristic(hap.Characteristic.Manufacturer, 'hassbian-abc')
.setCharacteristic(hap.Characteristic.Model, 'broadlink-mp1')
.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 switchService = acc.addService(hap.Service.Outlet, this.name + "s1", this.name + "s1");
var power0 = switchService.getCharacteristic(Characteristic.On);
var use0 = switchService.getCharacteristic(Characteristic.OutletInUse);
var switch1Service = acc.addService(hap.Service.Outlet, this.name + "s2", this.name + "s2");
var power1 = switch1Service.getCharacteristic(Characteristic.On);
var use1 = switch1Service.getCharacteristic(Characteristic.OutletInUse);
var switch2Service = acc.addService(hap.Service.Outlet, this.name + "s3", this.name + "s3");
var power2 = switch2Service.getCharacteristic(Characteristic.On);
var use2 = switch2Service.getCharacteristic(Characteristic.OutletInUse);
var switch3Service = acc.addService(hap.Service.Outlet, this.name + "s4", this.name + "s4");
var power3 = switch3Service.getCharacteristic(Characteristic.On);
var use3 = switch3Service.getCharacteristic(Characteristic.OutletInUse);
setInterval(function() {
getstate();
}, config.pollingTime * 1000);
power0
.updateValue(powered)
.on('set', (state, callback) => {
var s_index = "1";
var b = new broadlink();
if (state) {
if (powered) {
return callback(null, true);
} else {
b.discover();
b.on("deviceReady", (dev) => {
if (mac_buff(broadlinkmac).equals(dev.mac) || dev.host.address == broadlinkip) {
dev.set_power(s_index, true);
dev.exit();
clearInterval(checkAgainSet);
powered = true;
return callback(null, true);
} else {
dev.exit();
}
});
var checkAgainSet = setInterval(function() {
//self.log("Discovering Again for Set Command... " + self.sname);
b.discover();
}, 1000)
}
} else {
if (powered) {
b.discover();
b.on("deviceReady", (dev) => {
if (mac_buff(broadlinkmac).equals(dev.mac) || dev.host.address == broadlinkip) {
dev.set_power(s_index, false);
dev.exit();
clearInterval(checkAgainSet);
powered = false;
return callback(null, false);
} else {
dev.exit();
}
});
var checkAgainSet = setInterval(function() {
//self.log("Discovering Again for Set Command... " + self.sname);
b.discover();
}, 1000)
} else {
return callback(null, false)
}
}
})
.on('get', (callback) => {
callback(null, powered);
});
use0
.updateValue(powered)
.on('get', (callback) => {
callback(null, powered);
});
power1
.updateValue(powered1)
.on('set', (state, callback) => {
var s_index = "2";
var b = new broadlink();
if (state) {
if (powered1) {
return callback(null, true);
} else {
b.discover();
b.on("deviceReady", (dev) => {
if (mac_buff(broadlinkmac).equals(dev.mac) || dev.host.address == broadlinkip) {
dev.set_power(s_index, true);
dev.exit();
clearInterval(checkAgainSet);
powered1 = true;
return callback(null, true);
} else {
dev.exit();
}
});
var checkAgainSet = setInterval(function() {
//self.log("Discovering Again for Set Command... " + self.sname);
b.discover();
}, 1000)
}
} else {
if (powered1) {
b.discover();
b.on("deviceReady", (dev) => {
if (mac_buff(broadlinkmac).equals(dev.mac) || dev.host.address == broadlinkip) {
dev.set_power(s_index, false);
dev.exit();
clearInterval(checkAgainSet);
powered1 = false;
return callback(null, false);
} else {
dev.exit();
}
});
var checkAgainSet = setInterval(function() {
//self.log("Discovering Again for Set Command... " + self.sname);
b.discover();
}, 1000)
} else {
return callback(null, false)
}
}
})
.on('get', (callback) => {
callback(null, powered1);
});
use1
.updateValue(powered1)
.on('get', (callback) => {
callback(null, powered1);
});
power2
.updateValue(powered2)
.on('set', (state, callback) => {
var s_index = "3";
var b = new broadlink();
if (state) {
if (powered2) {
return callback(null, true);
} else {
b.discover();
b.on("deviceReady", (dev) => {
if (mac_buff(broadlinkmac).equals(dev.mac) || dev.host.address == broadlinkip) {
dev.set_power(s_index, true);
dev.exit();
clearInterval(checkAgainSet);
powered2 = true;
return callback(null, true);
} else {
dev.exit();
}
});
var checkAgainSet = setInterval(function() {
//self.log("Discovering Again for Set Command... " + self.sname);
b.discover();
}, 1000)
}
} else {
if (powered2) {
b.discover();
b.on("deviceReady", (dev) => {
if (mac_buff(broadlinkmac).equals(dev.mac) || dev.host.address == broadlinkip) {
dev.set_power(s_index, false);
dev.exit();
clearInterval(checkAgainSet);
powered2 = false;
return callback(null, false);
} else {
dev.exit();
}
});
var checkAgainSet = setInterval(function() {
//self.log("Discovering Again for Set Command... " + self.sname);
b.discover();
}, 1000)
} else {
return callback(null, false)
}
}
})
.on('get', (callback) => {
callback(null, powered2);
});
use2
.updateValue(powered2)
.on('get', (callback) => {
callback(null, powered2);
});
power3
.updateValue(powered3)
.on('set', (state, callback) => {
var s_index = "2";
var b = new broadlink();
if (state) {
if (powered3) {
return callback(null, true);
} else {
b.discover();
b.on("deviceReady", (dev) => {
if (mac_buff(broadlinkmac).equals(dev.mac) || dev.host.address == broadlinkip) {
dev.set_power(s_index, true);
dev.exit();
clearInterval(checkAgainSet);
powered3 = true;
return callback(null, true);
} else {
dev.exit();
}
});
var checkAgainSet = setInterval(function() {
//self.log("Discovering Again for Set Command... " + self.sname);
b.discover();
}, 1000)
}
} else {
if (powered3) {
b.discover();
b.on("deviceReady", (dev) => {
if (mac_buff(broadlinkmac).equals(dev.mac) || dev.host.address == broadlinkip) {
dev.set_power(s_index, false);
dev.exit();
clearInterval(checkAgainSet);
powered3 = false;
return callback(null, false);
} else {
dev.exit();
}
});
var checkAgainSet = setInterval(function() {
//self.log("Discovering Again for Set Command... " + self.sname);
b.discover();
}, 1000)
} else {
return callback(null, false)
}
}
})
.on('get', (callback) => {
callback(null, powered3);
});
use3
.updateValue(powered3)
.on('get', (callback) => {
callback(null, powered3);
});
var getstate = (function() {
var b = new broadlink();
b.discover();
b.on("deviceReady", (dev) => {
if (mac_buff(broadlinkmac).equals(dev.mac) || dev.host.address == broadlinkip) {
dev.check_power();
dev.on("mp_power", (status_array) => {
dev.exit();
//console.log("status_array", status_array);
if (!status_array[0]) {
powered = false;
} else {
powered = true;
}
if (!status_array[1]) {
powered1 = false;
} else {
powered1 = true;
}
if (!status_array[2]) {
powered2 = false;
} else {
powered2 = true;
}
if (!status_array[3]) {
powered3 = false;
} else {
powered3 = true;
}
power0.updateValue(powered);
power1.updateValue(powered1);
power2.updateValue(powered2);
power3.updateValue(powered3);
});
} else {
dev.exit();
}
});
});
this.log('publishing broadlink-mp1 ' + 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.OUTLET
});
acc._server.on('listening', () => {
this.log('broadlink-mp1 ' + this.name + ' listening on port ' + config.port);
this.status({fill: 'green', shape: 'ring', text: ' '});
});
acc._server.on('pair', username => {
this.log('broadlink-mp1 ' + this.name + ' paired', username);
});
acc._server.on('unpair', username => {
this.log('broadlink-mp1 ' + this.name + ' unpaired', username);
});
acc._server.on('verify', () => {
this.log('broadlink-mp1 ' + this.name + ' verify');
});
}).close();
})
.listen(config.port);
this.on('close', () => {
});
}
}
RED.nodes.registerType('hassbian-abc-homekit-broadlink-mp1', hassbianHomeKitbroadlink);
};