neramirez-broadlink-ts
Version:
A TypeScript-enhanced Node.JS fork of broadlinkjs, designed for interacting with RM devices in homebridge-broadlink-rm. Now includes a feature for handling multiple requests to the same device, with a specific focus on supporting homebridge-broadlink-wind
172 lines • 8.32 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Broadlink = void 0;
const events_1 = __importDefault(require("events"));
const dgram_1 = __importDefault(require("dgram"));
const os_1 = __importDefault(require("os"));
const assert_1 = __importDefault(require("assert"));
const device_types_1 = require("./device.types");
const broadLinkDeviceRF_1 = require("./device/broadLinkDeviceRF");
const broadLinkDevice_1 = require("./device/broadLinkDevice");
class Broadlink extends events_1.default {
constructor(logger) {
super();
this.discover = async () => {
// Close existing sockets
this.sockets.forEach((socket) => {
socket.close();
});
this.sockets = [];
// Open a UDP socket on each network interface/IP address
const ipAddresses = this.getIPAddresses();
ipAddresses.forEach((ipAddress) => {
const socket = dgram_1.default.createSocket({ type: "udp4", reuseAddr: true });
this.sockets.push(socket);
socket.on("listening", this.onListening.bind(this, socket, ipAddress));
socket.on("message", this.onMessage.bind(this));
socket.on("error", (err) => {
this.logger.error(`Error in UDP socket: ${err}`);
});
socket.bind(0, ipAddress);
});
return new Promise((resolve) => {
setTimeout(() => {
resolve(this.devices);
}, 10000);
});
};
this.getIPAddresses = () => {
const interfaces = os_1.default.networkInterfaces();
const ipAddresses = [];
Object.keys(interfaces).forEach((interfaceID) => {
const currentInterface = interfaces[interfaceID];
if (currentInterface) {
currentInterface.forEach((address) => {
if (address.family === "IPv4" && !address.internal) {
ipAddresses.push(address.address);
}
});
}
});
return ipAddresses;
};
this.onListening = (socket, ipAddress) => {
// Broadcast a multicast UDP message to let Broadlink devices know we're listening
socket.setBroadcast(true);
const splitIPAddress = ipAddress.split(".");
const port = socket.address().port;
this.logger.info(`Listening for Broadlink devices on ${ipAddress}:${port} (UDP)`);
const now = new Date();
const timezone = now.getTimezoneOffset() / -3600;
const packet = Buffer.alloc(0x30, 0);
const year = now.getFullYear();
if (timezone < 0) {
packet[0x08] = 0xff + timezone - 1;
packet[0x09] = 0xff;
packet[0x0a] = 0xff;
packet[0x0b] = 0xff;
}
else {
packet[0x08] = timezone;
packet[0x09] = 0;
packet[0x0a] = 0;
packet[0x0b] = 0;
}
packet[0x0c] = year & 0xff;
packet[0x0d] = year >> 8;
packet[0x0e] = now.getMinutes();
packet[0x0f] = now.getHours();
packet[0x10] = year % 100;
packet[0x11] = now.getDay();
packet[0x12] = now.getDate();
packet[0x13] = now.getMonth();
packet[0x18] = parseInt(splitIPAddress[0]);
packet[0x19] = parseInt(splitIPAddress[1]);
packet[0x1a] = parseInt(splitIPAddress[2]);
packet[0x1b] = parseInt(splitIPAddress[3]);
packet[0x1c] = port & 0xff;
packet[0x1d] = port >> 8;
packet[0x26] = 6;
let checksum = 0xbeaf;
for (let i = 0; i < packet.length; i++) {
checksum += packet[i];
}
checksum = checksum & 0xffff;
packet[0x20] = checksum & 0xff;
packet[0x21] = checksum >> 8;
socket.send(packet, 0, packet.length, 80, "255.255.255.255");
};
this.onMessage = (message, host) => {
this.logger.info(`Received a message from ${host.address}:${host.port} to broadcast message`);
// Broadlink device has responded
const macAddress = Buffer.alloc(6, 0);
message.copy(macAddress, 0x00, 0x3f);
message.copy(macAddress, 0x01, 0x3e);
message.copy(macAddress, 0x02, 0x3d);
message.copy(macAddress, 0x03, 0x3c);
message.copy(macAddress, 0x04, 0x3b);
message.copy(macAddress, 0x05, 0x3a);
// Ignore if we already know about this device
const key = macAddress.toString("hex");
if (this.devices[key])
return;
const deviceType = message[0x34] | (message[0x35] << 8);
// Create a Device instance
this.addDevice(host, macAddress, deviceType);
};
this.addDevice = (host, macAddressBuffer, deviceType) => {
const macAddress = macAddressBuffer.toString("hex");
this.logger.info(`Discovered Broadlink device at ${host.address} (${macAddress}) with device type ${deviceType}`);
//Ignore if we already know about this device
if (this.devices[macAddress])
return;
const isHostObjectValid = typeof host === "object" &&
(host.port || host.port === 0) &&
host.address;
(0, assert_1.default)(isHostObjectValid, `createDevice: host should be an object e.g. { address: '192.168.1.32', port: 80 }`);
(0, assert_1.default)(macAddress, `createDevice: A unique macAddress should be provided`);
(0, assert_1.default)(deviceType, `createDevice: A deviceType from the rmDeviceTypes, rm4DeviceTypes, rm4PlusDeviceTypes, or rmPlusDeviceTypes list should be provided`);
// Ignore devices that don't support infrared or RF.
if (device_types_1.unsupportedDeviceTypes[deviceType])
return null;
if (deviceType >= 0x7530 && deviceType <= 0x7918)
return null; // OEM branded SPMini2
// If we don't know anything about the device we ask the user to provide details so that
// we can handle it correctly.
const isKnownDevice = device_types_1.rmDeviceTypes[deviceType] ||
device_types_1.rmPlusDeviceTypes[deviceType] ||
device_types_1.rm4DeviceTypes[deviceType] ||
device_types_1.rm4PlusDeviceTypes[deviceType];
if (!isKnownDevice) {
this.logger.error(`We've discovered an unknown Broadlink device code (code: "${deviceType.toString(16)}"):"${host.address}".\n`);
return null;
}
// Dynamically add relevant RF methods if the device supports it
const isRFSupported = device_types_1.rmPlusDeviceTypes[(deviceType)] || device_types_1.rm4PlusDeviceTypes[(deviceType)];
if (isRFSupported) {
this.logger.info(`Adding RF Support to device ${macAddress.toString()} with type ${deviceType}`);
this.devices[macAddress] = new broadLinkDeviceRF_1.BroadLinkDeviceRF(host, macAddressBuffer, deviceType, this.logger);
}
else {
// The Broadlink device is something we can use.
this.devices[macAddress] = new broadLinkDevice_1.BroadLinkDevice(host, macAddressBuffer, deviceType, this.logger);
}
const device = this.devices[macAddress];
if (device) {
// Authenticate the device and let others know when it's ready.
device.authenticate();
}
};
this.getDevices = () => {
return this.devices;
};
this.devices = {};
this.sockets = [];
this.logger = logger;
}
}
exports.Broadlink = Broadlink;
//# sourceMappingURL=broadlink.js.map