node-red-contrib-broadlink-control
Version:
Node Red nodes to control the Broadlink RM devices and other Broadlink home automation nodes.
149 lines (131 loc) • 5.11 kB
JavaScript
let EventEmitter = require('events');
let dgram = require('dgram');
let os = require('os');
let crypto = require('crypto');
class Device {
constructor(host, mac, devType, timeout = 10) {
this.host = host;
this.mac = mac;
this.devType = devType;
// this.devName = devName;
// this.devCloud = devCloud;
this.emitter = new EventEmitter();
this.on = this.emitter.on;
this.emit = this.emitter.emit;
this.timeout = timeout;
this.count = Math.random() & 0xffff;
this.key = Buffer.from([0x09, 0x76, 0x28, 0x34, 0x3f, 0xe9, 0x9e, 0x23, 0x76, 0x5c, 0x15, 0x13, 0xac, 0xcf, 0x8b, 0x02]);
this.iv = Buffer.from([0x56, 0x2e, 0x17, 0x99, 0x6d, 0x09, 0x3d, 0x28, 0xdd, 0xb3, 0xba, 0x69, 0x5a, 0x2e, 0x6f, 0x58]);
this.id = Buffer.from([0, 0, 0, 0]);
this.cs = dgram.createSocket({ type: 'udp4', reuseAddr: true });
this.cs.on('listening', function () {
//this.cs.setBroadcast(true);
});
this.cs.on("message", (response, rinfo) => {
var enc_payload = Buffer.alloc(response.length - 0x38, 0);
response.copy(enc_payload, 0, 0x38);
var decipher = crypto.createDecipheriv('aes-128-cbc', this.key, this.iv);
decipher.setAutoPadding(false);
var payload = decipher.update(enc_payload);
var p2 = decipher.final();
if (p2) {
payload = Buffer.concat([payload, p2]);
}
if (!payload) {
return false;
}
var command = response[0x26];
var err = response[0x22] | (response[0x23] << 8);
if (err != 0) return;
if (command == 0xe9) {
this.key = Buffer.alloc(0x10, 0);
payload.copy(this.key, 0, 0x04, 0x14);
this.id = Buffer.alloc(0x04, 0);
payload.copy(this.id, 0, 0x00, 0x04);
this.emit("deviceReady");
} else if (command == 0xee) {
this.emit("payload", err, payload);
}
});
this.cs.bind();
this.type = "Unknown";
}
sendPacket(command, payload) {
this.count = (this.count + 1) & 0xffff;
var packet = Buffer.alloc(0x38, 0);
packet[0x00] = 0x5a;
packet[0x01] = 0xa5;
packet[0x02] = 0xaa;
packet[0x03] = 0x55;
packet[0x04] = 0x5a;
packet[0x05] = 0xa5;
packet[0x06] = 0xaa;
packet[0x07] = 0x55;
packet[0x24] = this.devType & 0xff;
packet[0x25] = this.devType >> 8;
packet[0x26] = command;
packet[0x28] = this.count & 0xff;
packet[0x29] = this.count >> 8;
packet[0x2a] = this.mac[0];
packet[0x2b] = this.mac[1];
packet[0x2c] = this.mac[2];
packet[0x2d] = this.mac[3];
packet[0x2e] = this.mac[4];
packet[0x2f] = this.mac[5];
packet[0x30] = this.id[0];
packet[0x31] = this.id[1];
packet[0x32] = this.id[2];
packet[0x33] = this.id[3];
var checksum = 0xbeaf;
for (var i = 0; i < payload.length; i++) {
checksum += payload[i];
checksum = checksum & 0xffff;
}
//console.log(payload); console.log(this.key, this.iv)
var cipher = crypto.createCipheriv('aes-128-cbc', this.key, this.iv);
payload = cipher.update(payload);
var p2 = cipher.final();
//console.log(packet);
//console.log(payload);
packet[0x34] = checksum & 0xff;
packet[0x35] = checksum >> 8;
packet = Buffer.concat([packet, payload]);
checksum = 0xbeaf;
for (var i = 0; i < packet.length; i++) {
checksum += packet[i];
checksum = checksum & 0xffff;
}
packet[0x20] = checksum & 0xff;
packet[0x21] = checksum >> 8;
this.cs.send(packet, 0, packet.length, this.host.port, this.host.address);
}
auth() {
var payload = Buffer.alloc(0x50, 0);
payload[0x04] = 0x31;
payload[0x05] = 0x31;
payload[0x06] = 0x31;
payload[0x07] = 0x31;
payload[0x08] = 0x31;
payload[0x09] = 0x31;
payload[0x0a] = 0x31;
payload[0x0b] = 0x31;
payload[0x0c] = 0x31;
payload[0x0d] = 0x31;
payload[0x0e] = 0x31;
payload[0x0f] = 0x31;
payload[0x10] = 0x31;
payload[0x11] = 0x31;
payload[0x12] = 0x31;
payload[0x1e] = 0x01;
payload[0x2d] = 0x01;
payload[0x30] = 'T'.charCodeAt(0);
payload[0x31] = 'e'.charCodeAt(0);
payload[0x32] = 's'.charCodeAt(0);
payload[0x33] = 't'.charCodeAt(0);
payload[0x34] = ' '.charCodeAt(0);
payload[0x35] = ' '.charCodeAt(0);
payload[0x36] = '1'.charCodeAt(0);
this.sendPacket(0x65, payload);
}
}
module.exports = Device;