node-red-contrib-victron-ble
Version:
node-red node to parse Instant Readout advertisement data from Victron BLE devices
93 lines (92 loc) • 3.58 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.Scanner = void 0;
const get_ble_adapter_1 = require("./ble/get-ble-adapter");
const devices_1 = require("./devices");
const events_1 = require("events");
class Scanner extends events_1.EventEmitter {
adapterMode;
ble = null;
deviceKeys = {};
deviceRawOptions = {};
knownDevices = {};
discovered = new Map();
constructor(adapterMode = 'auto') {
super();
this.adapterMode = adapterMode;
}
async start() {
this.ble = await (0, get_ble_adapter_1.getBleAdapter)(this.adapterMode);
if (!this.ble)
return;
this.ble.on('raw', (packet) => this.handleRawPacket(packet));
}
async stop() {
if (this.ble)
await this.ble.stopScan();
}
setSettings(address, key, includeRaw = false) {
this.deviceKeys[address.toLowerCase()] = key;
this.deviceRawOptions[address.toLowerCase()] = includeRaw;
delete this.knownDevices[address.toLowerCase()];
}
getDiscoveredDevices() {
const devices = new Map(this.discovered);
for (const device of this.ble?.getDiscoveredDevices() ?? []) {
const address = device.address.toLowerCase();
const existing = devices.get(address);
devices.set(address, {
address,
name: device.name ?? existing?.name ?? '',
rssi: device.rssi ?? existing?.rssi ?? 0,
});
}
return Array.from(devices.values());
}
handleRawPacket(packet) {
const now = Date.now();
const address = packet.address.toLowerCase();
let dev = this.discovered.get(address);
if (!dev) {
dev = { address, name: packet.name || '', rssi: packet.rssi };
this.discovered.set(address, dev);
}
else {
dev.name = packet.name || dev.name;
dev.rssi = packet.rssi;
}
// Try to parse if we have a key
if (this.deviceKeys[address]) {
let emitRaw = true;
try {
if (packet.rawData.length > 0 && packet.rawData[0] == 0x10) {
const deviceClass = (0, devices_1.detectDeviceType)(packet.rawData);
if (!deviceClass)
throw new Error(`Could not identify device type for ${address}`);
let device = this.knownDevices[address];
if (!device) {
device = new deviceClass(this.deviceKeys[address]);
this.knownDevices[address] = device;
}
const parsedDevice = device.parse(packet.rawData);
const payload = parsedDevice.toJson();
const parsedPacket = { ...packet, payload };
// copy decryptedData to packet if requested to send it out
if (this.deviceRawOptions[address]) {
parsedPacket.decryptedData = parsedPacket.payload.decryptedData.toString('hex');
}
delete parsedPacket.payload.decryptedData;
delete parsedPacket.rawData;
this.emit('parsed', parsedPacket);
emitRaw = false;
}
}
catch (error) {
// Parsing failed, emit as raw only
}
if (emitRaw)
this.emit('raw', packet);
}
}
}
exports.Scanner = Scanner;