zwave-js-ui
Version:
Z-Wave Control Panel and MQTT Gateway
213 lines (212 loc) • 7.67 kB
JavaScript
"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;