UNPKG

zwave-js-ui

Version:

Z-Wave Control Panel and MQTT Gateway

213 lines (212 loc) 7.67 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const zwave_js_1 = require("zwave-js"); const EventEmitter_1 = require("./EventEmitter"); const logger_1 = require("./logger"); const SocketEvents_1 = require("./SocketEvents"); const app_1 = require("../config/app"); const utils_1 = require("./utils"); const utils_2 = require("./utils"); const path_1 = require("path"); // eslint-disable-next-line @typescript-eslint/no-var-requires const loglevels = require('triple-beam').configs.npm.levels; const logger = (0, logger_1.module)('ZnifferManager'); const ZNIFFER_LOG_FILE = (0, utils_1.joinPath)(app_1.logsDir, 'zniffer_%DATE%.log'); const ZNIFFER_CAPTURE_FILE = (0, utils_1.joinPath)(app_1.storeDir, 'zniffer_capture_%DATE%.zlf'); class ZnifferManager extends EventEmitter_1.TypedEventEmitter { zniffer; config; socket; error; restartTimeout; get started() { return !!this.zniffer?.active; } constructor(config, socket) { super(); this.config = config; this.socket = socket; if (!config.enabled) { logger.info('Zniffer is DISABLED'); return; } const znifferOptions = { convertRSSI: config.convertRSSI, defaultFrequency: config.defaultFrequency, logConfig: { enabled: config.logEnabled, level: config.logLevel ? loglevels[config.logLevel] : 'info', logToFile: config.logToFile, filename: ZNIFFER_LOG_FILE, forceConsole: (0, utils_2.isDocker)() ? !config.logToFile : false, maxFiles: config.maxFiles || 7, nodeFilter: config.nodeFilter && config.nodeFilter.length > 0 ? config.nodeFilter.map((n) => parseInt(n)) : undefined, }, }; (0, utils_1.parseSecurityKeys)(config, znifferOptions); this.zniffer = new zwave_js_1.Zniffer(config.port, znifferOptions); this.zniffer.on('error', (error) => { this.onError(error); }); this.zniffer.on('frame', (frame, rawData) => { const socketFrame = this.parseFrame(frame, rawData); this.socket.emit(SocketEvents_1.socketEvents.znifferFrame, socketFrame); }); this.zniffer.on('corrupted frame', (frame, rawData) => { const socketFrame = this.parseFrame(frame, rawData); this.socket.emit(SocketEvents_1.socketEvents.znifferFrame, socketFrame); }); this.zniffer.on('ready', () => { logger.info('Zniffer ready'); }); logger.info('Initing Zniffer...'); this.init().catch(() => { }); } async init() { try { await this.zniffer.init(); } catch (error) { logger.info('Retrying in 5s...'); this.restartTimeout = setTimeout(() => { this.init().catch(() => { }); }, 5000); } this.onStateChange(); } parseFrame(frame, rawData, timestamp = Date.now()) { const socketFrame = { ...frame, corrupted: !('protocol' in frame), payload: '', timestamp, raw: (0, utils_1.buffer2hex)(rawData), }; if ('payload' in frame) { if (frame.payload instanceof zwave_js_1.CommandClass) { socketFrame.parsedPayload = this.ccToLogRecord(frame.payload); } else { socketFrame.payload = (0, utils_1.buffer2hex)(frame.payload); } } return socketFrame; } onError(error) { logger.error('Zniffer error:', error); this.error = error.message; this.onStateChange(); } onStateChange() { this.socket.emit(SocketEvents_1.socketEvents.znifferState, this.status()); } checkReady() { if (!this.config.enabled || !this.zniffer) { throw new Error('Zniffer is not initialized'); } } status() { return { error: this.error, started: this.started, supportedFrequencies: Object.fromEntries(this.zniffer?.supportedFrequencies ?? []), frequency: this.zniffer?.currentFrequency, lrRegions: Array.from(this.zniffer?.lrRegions ?? []), supportedLRChannelConfigs: Object.fromEntries(this.zniffer?.supportedLRChannelConfigs ?? []), lrChannelConfig: this.zniffer?.currentLRChannelConfig, }; } getFrames() { this.checkReady(); return this.zniffer.capturedFrames.map((frame) => { return this.parseFrame(frame.parsedFrame, Buffer.from(frame.frameData), frame.timestamp.getTime()); }); } async setFrequency(frequency) { this.checkReady(); logger.info(`Setting Zniffer frequency to ${frequency}`); await this.zniffer.setFrequency(frequency); this.onStateChange(); logger.info(`Zniffer frequency set to ${frequency}`); } async setLRChannelConfig(channelConfig) { this.checkReady(); logger.info(`Setting Zniffer LR channel configuration to ${channelConfig}`); await this.zniffer.setLRChannelConfig(channelConfig); this.onStateChange(); logger.info(`Zniffer LR channel configuration set to ${channelConfig}`); } ccToLogRecord(commandClass) { try { const parsed = commandClass.toLogEntry(); if ((0, zwave_js_1.isEncapsulatingCommandClass)(commandClass)) { parsed.encapsulated = [ this.ccToLogRecord(commandClass.encapsulated), ]; } else if ((0, zwave_js_1.isMultiEncapsulatingCommandClass)(commandClass)) { parsed.encapsulated = [ commandClass.encapsulated.map((cc) => this.ccToLogRecord(cc)), ]; } return parsed; } catch (error) { logger.error('Error parsing command class:', error); return { error: error.message, }; } } async close() { if (this.restartTimeout) clearTimeout(this.restartTimeout); if (this.zniffer) { this.zniffer.removeAllListeners(); await this.stop(); await this.zniffer.destroy(); } } async start() { this.checkReady(); if (this.started) { logger.info('Zniffer already started'); return; } logger.info('Starting...'); await this.zniffer.start(); this.onStateChange(); logger.info('Started'); } async stop() { this.checkReady(); if (!this.started) { logger.info('Zniffer is already stopped'); return; } logger.info('Stopping...'); await this.zniffer.stop(); this.onStateChange(); logger.info('Stopped'); } clear() { this.checkReady(); logger.info('Clearing...'); this.zniffer.clearCapturedFrames(); logger.info('Frames cleared'); } async saveCaptureToFile() { this.checkReady(); const filePath = ZNIFFER_CAPTURE_FILE.replace('%DATE%', new Date().toISOString()); logger.info(`Saving capture to ${filePath}`); await this.zniffer.saveCaptureToFile(filePath); logger.info('Capture saved'); return { path: filePath, name: (0, path_1.basename)(filePath), }; } } exports.default = ZnifferManager;