diff --git a/plejd/Dockerfile b/plejd/Dockerfile index b8d2554..793b6a6 100644 --- a/plejd/Dockerfile +++ b/plejd/Dockerfile @@ -8,11 +8,12 @@ SHELL ["/bin/bash", "-o", "pipefail", "-c"] # Copy data for add-on COPY ./api.js /plejd/ +COPY ./ble.bluez.js /plejd/ COPY ./config.json /plejd/ +COPY ./Logger.js /plejd/ COPY ./main.js /plejd/ COPY ./mqtt.js /plejd/ COPY ./package.json /plejd/ -COPY ./ble.bluez.js /plejd/ COPY ./scene.manager.js /plejd/ ARG BUILD_ARCH diff --git a/plejd/Logger.js b/plejd/Logger.js new file mode 100644 index 0000000..c606a83 --- /dev/null +++ b/plejd/Logger.js @@ -0,0 +1,99 @@ +const winston = require("winston"); +const { colorize, combine, label, printf, timestamp } = winston.format; + +const logFormat = printf(info => { + if(info.stack) { + return `${info.timestamp} ${info.level} [${info.label}] ${info.message}\n${info.stack}`; + } + return `${info.timestamp} ${info.level} [${info.label}] ${info.message}`; +}); + +/** Winston-based logger */ +class Logger { + constructor () { + throw new Error("Please call createLogger instead"); + } + + /** Created logger will follow Winston createLogger, but + * - add module name to logger + * - swap debug/verbose levels and omit http to mimic HA standard + * Levels (in order): error, warn, info, debug, verbose, silly + * */ + static getLogger(moduleName, level="verbose") { + const logger = winston.createLogger({ + format: combine( + winston.format(info => { + switch (info.level) { + case "verbose": + info.level = "VRB"; + break; + case "debug": + info.level = "DBG"; + break; + default: + info.level = info.level.substring(0,3).toUpperCase() + } + + return info; + })(), + timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }), + label({ label: moduleName}), + colorize(), + logFormat, + ), + level: level, + levels: Logger.logLevels().levels, + transports: [ + new winston.transports.Console(), + ] + }); + winston.addColors(Logger.logLevels().colors); + return logger; + } + + + static logLevels() { + // Default (npm) levels + // levels = { + // error: 0, + // warn: 1, + // info: 2, + // http: 3, + // verbose: 4, + // debug: 5, + // silly: 6 + // } + // colors = { + // error: 'red', + // warn: 'yellow', + // info: 'green', + // http: 'green', + // verbose: 'cyan', + // debug: 'blue', + // silly: 'magenta' + // }; + + // Mimic HA standard below + // Debug/verbose swapped compared to npm levels, http omitted + return { + levels: { + error: 0, + warn: 1, + info: 2, + debug: 3, + verbose: 4, + silly: 6 + }, + colors: { + error: 'red', + warn: 'yellow', + info: 'green', + debug: 'cyan', + verbose: 'blue', + silly: 'magenta' + } + }; + } +} + +module.exports = Logger; diff --git a/plejd/api.js b/plejd/api.js index 8abd419..fd4bd88 100644 --- a/plejd/api.js +++ b/plejd/api.js @@ -1,5 +1,6 @@ const axios = require('axios'); const EventEmitter = require('events'); +const Logger = require('./Logger'); API_APP_ID = 'zHtVqXt8k4yFyk2QGmgp48D9xZr2G94xWYnF4dak'; API_BASE_URL = 'https://cloud.plejd.com/parse/'; @@ -7,20 +8,7 @@ API_LOGIN_URL = 'login'; API_SITE_LIST_URL = 'functions/getSiteList'; API_SITE_DETAILS_URL = 'functions/getSiteById'; -const logInfo = true; // Normal operations -const logDebug = false; // Chatty -const logVerbose = false; // Very chatty - -const consoleLogger = (level) => (...msg) => - console.log(new Date().toISOString().replace("T", " ").substring(0, 19) + "Z", level, "plejd-api", ...msg); - -const getLogger = (level, shouldLog) => (shouldLog ? consoleLogger(level) : () => {}); - -const errLogger = getLogger("ERR", true); -const infLogger = getLogger("INF", logInfo); -const dbgLogger = getLogger("DBG", logDebug); -const vrbLogger = getLogger("vrb", logVerbose); - +const logger = Logger.getLogger("plejd-api"); class PlejdApi extends EventEmitter { constructor(siteName, username, password, includeRoomsAsLights) { @@ -35,13 +23,9 @@ class PlejdApi extends EventEmitter { this.site = null; } - updateSettings(settings) { - logVerbose("Got new settings: ", settings); - } - login() { - infLogger('login()'); - infLogger('logging into ' + this.siteName); + logger.info('login()'); + logger.info('logging into ' + this.siteName); const self = this; const instance = axios.create({ @@ -53,7 +37,7 @@ class PlejdApi extends EventEmitter { }); return new Promise((resolve, reject) => { - dbgLogger('sending POST to ' + API_BASE_URL + API_LOGIN_URL); + logger.debug('sending POST to ' + API_BASE_URL + API_LOGIN_URL); instance.post( API_LOGIN_URL, @@ -62,11 +46,11 @@ class PlejdApi extends EventEmitter { 'password': this.password }) .then((response) => { - infLogger('got session token response'); + logger.info('got session token response'); self.sessionToken = response.data.sessionToken; if (!self.sessionToken) { - errLogger('No session token received'); + logger.error('No session token received'); reject('no session token received.'); } @@ -74,10 +58,10 @@ class PlejdApi extends EventEmitter { }) .catch((error) => { if (error.response.status === 400) { - errLogger('Server returned status 400. probably invalid credentials, please verify.'); + logger.error('Server returned status 400. probably invalid credentials, please verify.'); } else { - errLogger('Unable to retrieve session token response: ' + error); + logger.error('Unable to retrieve session token response: ', error); } reject('unable to retrieve session token response: ' + error); @@ -86,7 +70,7 @@ class PlejdApi extends EventEmitter { } getSites() { - infLogger('getSites()'); + logger.info('Get all Plejd sites for account...'); const self = this; const instance = axios.create({ @@ -99,15 +83,15 @@ class PlejdApi extends EventEmitter { }); return new Promise((resolve, reject) => { - dbgLogger('sending POST to ' + API_BASE_URL + API_SITE_LIST_URL); + logger.debug('sending POST to ' + API_BASE_URL + API_SITE_LIST_URL); instance.post(API_SITE_LIST_URL) .then((response) => { - infLogger('got site list response'); + logger.info('got site list response'); const site = response.data.result.find(x => x.site.title == self.siteName); if (!site) { - errLogger('error: failed to find a site named ' + self.siteName); + logger.error('error: failed to find a site named ' + self.siteName); reject('failed to find a site named ' + self.siteName); return; } @@ -115,14 +99,14 @@ class PlejdApi extends EventEmitter { resolve(site); }) .catch((error) => { - errLogger('error: unable to retrieve list of sites. error: ' + error); + logger.error('error: unable to retrieve list of sites. error: ', error); return reject('plejd-api: unable to retrieve list of sites. error: ' + error); }); }); } getSite(siteId) { - infLogger('getSite(...)'); + logger.info(`Get site details...`); const self = this; const instance = axios.create({ @@ -135,14 +119,14 @@ class PlejdApi extends EventEmitter { }); return new Promise((resolve, reject) => { - dbgLogger('sending POST to ' + API_BASE_URL + API_SITE_DETAILS_URL); + logger.debug('sending POST to ' + API_BASE_URL + API_SITE_DETAILS_URL); instance.post(API_SITE_DETAILS_URL, { siteId: siteId }) .then((response) => { - infLogger('got site details response'); + logger.info('got site details response'); if (response.data.result.length === 0) { const msg = 'no site with ID ' + siteId + ' was found.'; - errLogger('error: ' + msg); + logger.error('error: ' + msg); reject(msg); return; } @@ -153,7 +137,7 @@ class PlejdApi extends EventEmitter { resolve(self.cryptoKey); }) .catch((error) => { - errLogger('error: unable to retrieve the crypto key. error: ' + error); + logger.error('error: unable to retrieve the crypto key. error: ', error); return reject('plejd-api: unable to retrieve the crypto key. error: ' + error); }); }); @@ -162,7 +146,7 @@ class PlejdApi extends EventEmitter { getDevices() { let devices = []; - vrbLogger(JSON.stringify(this.site)); + logger.verbose(JSON.stringify(this.site)); const roomDevices = {}; @@ -252,7 +236,7 @@ class PlejdApi extends EventEmitter { } if (this.includeRoomsAsLights) { - dbgLogger('includeRoomsAsLights is set to true, adding rooms too.'); + logger.debug('includeRoomsAsLights is set to true, adding rooms too.'); for (let i = 0; i < this.site.rooms.length; i++) { const room = this.site.rooms[i]; const roomId = room.roomId; @@ -268,7 +252,7 @@ class PlejdApi extends EventEmitter { devices.push(newDevice); } - dbgLogger('includeRoomsAsLights done.'); + logger.debug('includeRoomsAsLights done.'); } // add scenes as switches diff --git a/plejd/ble.bluez.js b/plejd/ble.bluez.js index 54f15b9..045419a 100644 --- a/plejd/ble.bluez.js +++ b/plejd/ble.bluez.js @@ -2,20 +2,9 @@ const dbus = require('dbus-next'); const crypto = require('crypto'); const xor = require('buffer-xor'); const EventEmitter = require('events'); +const Logger = require('./Logger'); -const logInfo = true; // Normal operations -const logDebug = false; // Chatty -const logVerbose = false; // Very chatty - -const consoleLogger = (level) => (...msg) => - console.log(new Date().toISOString().replace('T', ' ').substring(0, 19) + 'Z', level, 'plejd-ble', ...msg); - -const getLogger = (level, shouldLog) => (shouldLog ? consoleLogger(level) : () => {}); - -const errLogger = getLogger('ERR', true); -const infLogger = getLogger('INF', logInfo); -const dbgLogger = getLogger('DBG', logDebug); -const vrbLogger = getLogger('vrb', logVerbose); +const logger = Logger.getLogger("plejd-ble"); // UUIDs @@ -46,7 +35,7 @@ class PlejdService extends EventEmitter { constructor(cryptoKey, devices, sceneManager, connectionTimeout, writeQueueWaitTime, keepAlive = false) { super(); - infLogger('Starting Plejd BLE, resetting all device states.'); + logger.info('Starting Plejd BLE, resetting all device states.'); this.cryptoKey = Buffer.from(cryptoKey.replace(/-/g, ''), 'hex'); @@ -76,7 +65,7 @@ class PlejdService extends EventEmitter { this.bus = dbus.systemBus(); this.adapter = null; - dbgLogger('wiring events and waiting for BLE interface to power up.'); + logger.debug('wiring events and waiting for BLE interface to power up.'); this.wireEvents(); } @@ -98,7 +87,7 @@ class PlejdService extends EventEmitter { clearInterval(this.pingRef); clearTimeout(this.writeQueueRef); - infLogger('init()'); + logger.info('init()'); const bluez = await this.bus.getProxyObject(BLUEZ_SERVICE_NAME, '/'); this.objectManager = await bluez.getInterface(DBUS_OM_INTERFACE); @@ -112,7 +101,7 @@ class PlejdService extends EventEmitter { } if (!this.adapter) { - errLogger('unable to find a bluetooth adapter that is compatible.'); + logger.error('unable to find a bluetooth adapter that is compatible.'); return; } @@ -126,7 +115,7 @@ class PlejdService extends EventEmitter { const connected = managedObjects[path][BLUEZ_DEVICE_ID].Connected.value; if (connected) { - infLogger('disconnecting ' + path); + logger.info('disconnecting ' + path); await device.Disconnect(); } @@ -144,22 +133,22 @@ class PlejdService extends EventEmitter { try { await this.adapter.StartDiscovery(); } catch (err) { - errLogger('failed to start discovery. Make sure no other add-on is currently scanning.'); + logger.error('failed to start discovery. Make sure no other add-on is currently scanning.'); return; } return new Promise(resolve => setTimeout(() => resolve( - this._internalInit().catch((err) => { errLogger('InternalInit exception! Will rethrow.', err); throw err; }) + this._internalInit().catch((err) => { logger.error('InternalInit exception! Will rethrow.', err); throw err; }) ), this.connectionTimeout * 1000 ) ); } async _internalInit() { - dbgLogger(`Got ${this.bleDevices.length} device(s).`); + logger.debug(`Got ${this.bleDevices.length} device(s).`); for (const plejd of this.bleDevices) { - dbgLogger(`Inspecting ${plejd['path']}`); + logger.debug(`Inspecting ${plejd['path']}`); try { const proxyObject = await this.bus.getProxyObject(BLUEZ_SERVICE_NAME, plejd['path']); @@ -174,9 +163,9 @@ class PlejdService extends EventEmitter { fixedPlejdPath = fixedPlejdPath.replace(/_/g, ''); plejd['device'] = this.devices.find(x => x.serialNumber === fixedPlejdPath); - dbgLogger(`Discovered ${plejd['path']} with rssi ${plejd['rssi']}`); + logger.debug(`Discovered ${plejd['path']} with rssi ${plejd['rssi']}`); } catch (err) { - errLogger(`Failed inspecting ${plejd['path']}. `, err); + logger.error(`Failed inspecting ${plejd['path']}. `, err); } } @@ -186,13 +175,13 @@ class PlejdService extends EventEmitter { for (const plejd of sortedDevices) { try { if (plejd['instance']) { - infLogger(`Connecting to ${plejd['path']}`); + logger.info(`Connecting to ${plejd['path']}`); await plejd['instance'].Connect(); connectedDevice = plejd; break } } catch (err) { - errLogger('Warning: unable to connect, will retry. ', err); + logger.error('Warning: unable to connect, will retry. ', err); } } @@ -208,12 +197,12 @@ class PlejdService extends EventEmitter { for (let path of managedPaths) { const pathInterfaces = Object.keys(managedObjects[path]); if (pathInterfaces.indexOf(iface) > -1) { - dbgLogger(`Found BLE interface '${iface}' at ${path}`); + logger.debug(`Found BLE interface '${iface}' at ${path}`); try { const adapterObject = await this.bus.getProxyObject(BLUEZ_SERVICE_NAME, path); return [path, adapterObject.getInterface(iface), adapterObject]; } catch (err) { - errLogger(`Failed to get interface '${iface}'. `, err); + logger.error(`Failed to get interface '${iface}'. `, err); } } } @@ -227,34 +216,25 @@ class PlejdService extends EventEmitter { if (interfaceKeys.indexOf(BLUEZ_DEVICE_ID) > -1) { if (interfaces[BLUEZ_DEVICE_ID]['UUIDs'].value.indexOf(PLEJD_SERVICE) > -1) { - dbgLogger(`Found Plejd service on ${path}`); + logger.debug(`Found Plejd service on ${path}`); this.bleDevices.push({ 'path': path }); } else { - errLogger('Uh oh, no Plejd device!'); + logger.error('Uh oh, no Plejd device!'); } } } - updateSettings(settings) { - dbgLogger('Got new settings: ', settings); - if (settings.debug) { - debug = true; - } else { - debug = false; - } - } - turnOn(deviceId, command) { - const deviceName = (logVerbose || logDebug) ? this._getDeviceName(deviceId) : ''; - infLogger(`Plejd got turn on command for ${deviceName} (${deviceId}), brightness ${command.brightness}${command.transition ? `, transition: ${command.transition}` : ''}`); + const deviceName = this._getDeviceName(deviceId); + logger.info(`Plejd got turn on command for ${deviceName} (${deviceId}), brightness ${command.brightness}${command.transition ? `, transition: ${command.transition}` : ''}`); this._transitionTo(deviceId, command.brightness, command.transition, deviceName); } turnOff(deviceId, command) { - const deviceName = (logVerbose || logDebug) ? this._getDeviceName(deviceId) : ''; - infLogger(`Plejd got turn off command for ${deviceName} (${deviceId}), brightness ${command.brightness}${command.transition ? `, transition: ${command.transition}` : ''}`); + const deviceName = this._getDeviceName(deviceId); + logger.info(`Plejd got turn off command for ${deviceName} (${deviceId}), brightness ${command.brightness}${command.transition ? `, transition: ${command.transition}` : ''}`); this._transitionTo(deviceId, 0, command.transition, deviceName); } @@ -281,8 +261,8 @@ class PlejdService extends EventEmitter { const transitionSteps = Math.min(Math.abs(deltaBrightness), MAX_TRANSITION_STEPS_PER_SECOND * transition); const transitionInterval = transition * 1000 / transitionSteps; - dbgLogger(`transitioning from ${initialBrightness} to ${targetBrightness} ${transition ? 'in ' + transition + ' seconds' : ''}.`); - vrbLogger(`delta brightness ${deltaBrightness}, steps ${transitionSteps}, interval ${transitionInterval} ms`); + logger.debug(`transitioning from ${initialBrightness} to ${targetBrightness} ${transition ? 'in ' + transition + ' seconds' : ''}.`); + logger.verbose(`delta brightness ${deltaBrightness}, steps ${transitionSteps}, interval ${transitionInterval} ms`); const dtStart = new Date(); @@ -302,11 +282,11 @@ class PlejdService extends EventEmitter { nSteps++; this._clearDeviceTransitionTimer(deviceId); newBrightness = targetBrightness; - dbgLogger(`Queueing finalize ${deviceName} (${deviceId}) transition from ${initialBrightness} to ${targetBrightness} in ${tElapsedMs}ms. Done steps ${nSteps}. Average interval ${tElapsedMs / (nSteps || 1)} ms.`); + logger.debug(`Queueing finalize ${deviceName} (${deviceId}) transition from ${initialBrightness} to ${targetBrightness} in ${tElapsedMs}ms. Done steps ${nSteps}. Average interval ${tElapsedMs / (nSteps || 1)} ms.`); this._setBrightness(deviceId, newBrightness, true, deviceName); } else { nSteps++; - vrbLogger(`Queueing dim transition for ${deviceName} (${deviceId}) to ${newBrightness}. Total queue length ${this.writeQueue.length}`); + logger.verbose(`Queueing dim transition for ${deviceName} (${deviceId}) to ${newBrightness}. Total queue length ${this.writeQueue.length}`); this._setBrightness(deviceId, newBrightness, false, deviceName); } @@ -314,7 +294,7 @@ class PlejdService extends EventEmitter { } else { if (transition && isDimmable) { - dbgLogger(`Could not transition light change. Either initial value is unknown or change is too small. Requested from ${initialBrightness} to ${targetBrightness}`) + logger.debug(`Could not transition light change. Either initial value is unknown or change is too small. Requested from ${initialBrightness} to ${targetBrightness}`) } this._setBrightness(deviceId, targetBrightness, true, deviceName); } @@ -325,13 +305,13 @@ class PlejdService extends EventEmitter { let log = ''; if (!brightness && brightness !== 0) { - dbgLogger(`Queueing turn on ${deviceName} (${deviceId}). No brightness specified, setting DIM to previous.`); + logger.debug(`Queueing turn on ${deviceName} (${deviceId}). No brightness specified, setting DIM to previous.`); payload = Buffer.from((deviceId).toString(16).padStart(2, '0') + '0110009701', 'hex'); log = 'ON'; } else { if (brightness <= 0) { - dbgLogger(`Queueing turn off ${deviceId}`); + logger.debug(`Queueing turn off ${deviceId}`); payload = Buffer.from((deviceId).toString(16).padStart(2, '0') + '0110009700', 'hex'); log = 'OFF'; } @@ -340,7 +320,7 @@ class PlejdService extends EventEmitter { brightness = 255; } - dbgLogger(`Queueing ${deviceId} set brightness to ${brightness}`); + logger.debug(`Queueing ${deviceId} set brightness to ${brightness}`); const brightnessVal = (brightness << 8) | brightness; payload = Buffer.from((deviceId).toString(16).padStart(2, '0') + '0110009801' + (brightnessVal).toString(16).padStart(4, '0'), 'hex'); log = `DIM ${brightness}`; @@ -351,23 +331,23 @@ class PlejdService extends EventEmitter { triggerScene(sceneIndex) { const sceneName = this._getDeviceName(sceneIndex); - infLogger(`Triggering scene ${sceneName} (${sceneIndex}). Scene name might be misleading if there is a device with the same numeric id.`); + logger.info(`Triggering scene ${sceneName} (${sceneIndex}). Scene name might be misleading if there is a device with the same numeric id.`); this.sceneManager.executeScene(sceneIndex, this); } async authenticate() { - infLogger('authenticate()'); + logger.info('authenticate()'); try { - dbgLogger('Sending challenge to device'); + logger.debug('Sending challenge to device'); await this.characteristics.auth.WriteValue([0], {}); - dbgLogger('Reading response from device'); + logger.debug('Reading response from device'); const challenge = await this.characteristics.auth.ReadValue({}); const response = this._createChallengeResponse(this.cryptoKey, Buffer.from(challenge)); - dbgLogger('Responding to authenticate'); + logger.debug('Responding to authenticate'); await this.characteristics.auth.WriteValue([...response], {}); } catch (err) { - errLogger('Failed to authenticate: ', err); + logger.error('Failed to authenticate: ', err); } // auth done, start ping @@ -382,11 +362,11 @@ class PlejdService extends EventEmitter { async throttledInit(delay) { if(this.initInProgress){ - dbgLogger('ThrottledInit already in progress. Skipping this call and returning existing promise.') + logger.debug('ThrottledInit already in progress. Skipping this call and returning existing promise.') return this.initInProgress; } this.initInProgress = new Promise((resolve) => setTimeout(async () => { - const result = await this.init().catch((err) => { errLogger('TrottledInit exception calling init(). Will re-throw.', err); throw err; }); + const result = await this.init().catch((err) => { logger.error('TrottledInit exception calling init(). Will re-throw.', err); throw err; }); this.initInProgress = null; resolve(result) }, delay)) @@ -395,20 +375,20 @@ class PlejdService extends EventEmitter { async write(data) { if (!data || !this.plejdService || !this.characteristics.data) { - dbgLogger('data, plejdService or characteristics not available. Cannot write()'); + logger.debug('data, plejdService or characteristics not available. Cannot write()'); return; } try { - vrbLogger(`Sending ${data.length} byte(s) of data to Plejd`, data); + logger.verbose(`Sending ${data.length} byte(s) of data to Plejd`, data); const encryptedData = this._encryptDecrypt(this.cryptoKey, this.plejdService.addr, data); await this.characteristics.data.WriteValue([...encryptedData], {}); return true; } catch (err) { if (err.message === 'In Progress') { - dbgLogger('Write failed due to \'In progress\' ', err); + logger.debug('Write failed due to \'In progress\' ', err); } else { - dbgLogger('Write failed ', err); + logger.debug('Write failed ', err); } await this.throttledInit(this.connectionTimeout * 1000); return false; @@ -416,29 +396,29 @@ class PlejdService extends EventEmitter { } startPing() { - infLogger('startPing()'); + logger.info('startPing()'); clearInterval(this.pingRef); this.pingRef = setInterval(async () => { - vrbLogger('ping'); + logger.silly('ping'); await this.ping(); }, 3000); } onPingSuccess(nr) { - vrbLogger('pong: ' + nr); + logger.silly('pong: ' + nr); } async onPingFailed(error) { - dbgLogger('onPingFailed(' + error + ')'); - infLogger('ping failed, reconnecting.'); + logger.debug('onPingFailed(' + error + ')'); + logger.info('ping failed, reconnecting.'); clearInterval(this.pingRef); await this.init(); } async ping() { - vrbLogger('ping()'); + logger.silly('ping()'); var ping = crypto.randomBytes(1); let pong = null; @@ -447,13 +427,13 @@ class PlejdService extends EventEmitter { await this.characteristics.ping.WriteValue([...ping], {}); pong = await this.characteristics.ping.ReadValue({}); } catch (err) { - errLogger('writing to plejd: ', err); + logger.error('Error writing to plejd: ', err); this.emit('pingFailed', 'write error'); return; } if (((ping[0] + 1) & 0xff) !== pong[0]) { - errLogger('plejd ping failed'); + logger.error('Plejd ping failed'); this.emit('pingFailed', 'plejd ping failed ' + ping[0] + ' - ' + pong[0]); return; } @@ -462,7 +442,7 @@ class PlejdService extends EventEmitter { } startWriteQueue() { - infLogger('startWriteQueue()'); + logger.info('startWriteQueue()'); clearTimeout(this.writeQueueRef); this.writeQueueRef = setTimeout(() => this.runWriteQueue(), this.writeQueueWaitTime); @@ -473,22 +453,22 @@ class PlejdService extends EventEmitter { while (this.writeQueue.length > 0) { const queueItem = this.writeQueue.pop(); const deviceName = this._getDeviceName(queueItem.deviceId); - dbgLogger(`Write queue: Processing ${deviceName} (${queueItem.deviceId}). Command ${queueItem.log}. Total queue length: ${this.writeQueue.length}`); + logger.debug(`Write queue: Processing ${deviceName} (${queueItem.deviceId}). Command ${queueItem.log}. Total queue length: ${this.writeQueue.length}`); if (this.writeQueue.some((item) => item.deviceId === queueItem.deviceId)) { - vrbLogger(`Skipping ${deviceName} (${queueItem.deviceId}) ${queueItem.log} due to more recent command in queue.`); + logger.verbose(`Skipping ${deviceName} (${queueItem.deviceId}) ${queueItem.log} due to more recent command in queue.`); continue; // Skip commands if new ones exist for the same deviceId, but still process all messages in order } const success = await this.write(queueItem.payload); if (!success && queueItem.shouldRetry) { queueItem.retryCount = (queueItem.retryCount || 0) + 1; - dbgLogger('Will retry command, count failed so far', queueItem.retryCount); + logger.debug(`Will retry command, count failed so far ${queueItem.retryCount}`); if (queueItem.retryCount <= MAX_RETRY_COUNT) { this.writeQueue.push(queueItem); // Add back to top of queue to be processed next; } else { - errLogger(`Write queue: Exceeed max retry count (${MAX_RETRY_COUNT}) for ${deviceName} (${queueItem.deviceId}). Command ${queueItem.log} failed.`); + logger.error(`Write queue: Exceeed max retry count (${MAX_RETRY_COUNT}) for ${deviceName} (${queueItem.deviceId}). Command ${queueItem.log} failed.`); break; } if (queueItem.retryCount > 1) { @@ -497,7 +477,7 @@ class PlejdService extends EventEmitter { } } } catch (e) { - errLogger('Error in writeQueue loop, values probably not written to Plejd', e); + logger.error('Error in writeQueue loop, values probably not written to Plejd', e); } this.writeQueueRef = setTimeout(() => this.runWriteQueue(), this.writeQueueWaitTime); @@ -510,7 +490,7 @@ class PlejdService extends EventEmitter { const uuid = (await properties.Get(GATT_SERVICE_ID, 'UUID')).value; if (uuid !== PLEJD_SERVICE) { - errLogger('not a Plejd device.'); + logger.error('not a Plejd device.'); return null; } @@ -534,17 +514,17 @@ class PlejdService extends EventEmitter { const chUuid = (await prop.Get(GATT_CHRC_ID, 'UUID')).value; if (chUuid === DATA_UUID) { - dbgLogger('found DATA characteristic.'); + logger.debug('found DATA characteristic.'); this.characteristics.data = ch; } else if (chUuid === LAST_DATA_UUID) { - dbgLogger('found LAST_DATA characteristic.'); + logger.debug('found LAST_DATA characteristic.'); this.characteristics.lastData = ch; this.characteristics.lastDataProperties = prop; } else if (chUuid === AUTH_UUID) { - dbgLogger('found AUTH characteristic.'); + logger.debug('found AUTH characteristic.'); this.characteristics.auth = ch; } else if (chUuid === PING_UUID) { - dbgLogger('found PING characteristic.'); + logger.debug('found PING characteristic.'); this.characteristics.ping = ch; } } @@ -555,10 +535,10 @@ class PlejdService extends EventEmitter { } async onDeviceConnected(device) { - infLogger('onDeviceConnected()'); - dbgLogger('Device: ', device); + logger.info('onDeviceConnected()'); + logger.debug(`Device: ${device}`); if (!device) { - errLogger('Device is null. Should we break/return when this happens?'); + logger.error('Device is null. Should we break/return when this happens?'); } const objects = await this.objectManager.GetManagedObjects(); @@ -582,7 +562,7 @@ class PlejdService extends EventEmitter { } } - infLogger('trying ' + chPaths.length + ' characteristics'); + logger.info('trying ' + chPaths.length + ' characteristics'); this.plejdService = await this._processPlejdService(path, chPaths); if (this.plejdService) { @@ -592,13 +572,13 @@ class PlejdService extends EventEmitter { } if (!this.plejdService) { - infLogger('warning: wasn\'t able to connect to Plejd, will retry.'); + logger.info('warning: wasn\'t able to connect to Plejd, will retry.'); this.emit('connectFailed'); return; } if (!this.characteristics.auth) { - errLogger('unable to enumerate characteristics.'); + logger.error('unable to enumerate characteristics.'); this.emit('connectFailed'); return; } @@ -632,19 +612,19 @@ class PlejdService extends EventEmitter { const data2 = parseInt(decoded.toString('hex', 6, 8), 16) >> 8; if (decoded.length < 5) { - dbgLogger('Too short raw event ignored: ', decoded.toString('hex')); + logger.debug(`Too short raw event ignored: ${decoded.toString('hex')}`); // ignore the notification since too small return; } - const deviceName = (logVerbose || logDebug) ? this._getDeviceName(deviceId) : ''; - vrbLogger('Raw event received: ', decoded.toString('hex')); - vrbLogger(`Device ${deviceId}, cmd ${cmd.toString('hex')}, state ${state}, dim/data2 ${data2}`); + const deviceName = this._getDeviceName(deviceId); + logger.verbose(`Raw event received: ${decoded.toString('hex')}`); + logger.verbose(`Device ${deviceId}, cmd ${cmd.toString('hex')}, state ${state}, dim/data2 ${data2}`); if (cmd === BLE_CMD_DIM_CHANGE || cmd === BLE_CMD_DIM2_CHANGE) { const dim = data2; - dbgLogger(`${deviceName} (${deviceId}) got state+dim update. S: ${state}, D: ${dim}`); + logger.debug(`${deviceName} (${deviceId}) got state+dim update. S: ${state}, D: ${dim}`); this.emit('stateChanged', deviceId, { state: state, @@ -655,9 +635,9 @@ class PlejdService extends EventEmitter { state: state, dim: dim }; - vrbLogger('All states: ', this.plejdDevices); + logger.verbose(`All states: ${JSON.stringify(this.plejdDevices)}`); } else if (cmd === BLE_CMD_STATE_CHANGE) { - dbgLogger(`${deviceName} (${deviceId}) got state update. S: ${state}`); + logger.debug(`${deviceName} (${deviceId}) got state update. S: ${state}`); this.emit('stateChanged', deviceId, { state: state }); @@ -665,25 +645,25 @@ class PlejdService extends EventEmitter { state: state, dim: 0 }; - vrbLogger('All states: ', this.plejdDevices); + logger.verbose(`All states: ${this.plejdDevices}`); } else if (cmd === BLE_CMD_SCENE_TRIG) { const sceneId = parseInt(decoded.toString('hex', 5, 6), 16); const sceneName = this._getDeviceName(sceneId); - dbgLogger(`${sceneName} (${sceneId}) scene triggered (device id ${deviceId}). Name can be misleading if there is a device with the same numeric id.`); + logger.debug(`${sceneName} (${sceneId}) scene triggered (device id ${deviceId}). Name can be misleading if there is a device with the same numeric id.`); this.emit('sceneTriggered', deviceId, sceneId); } else if (cmd === '001b') { - // vrbLogger('Command 001b seems to be some kind of often repeating ping/mesh data'); + logger.silly('Command 001b seems to be some kind of often repeating ping/mesh data'); } else { - vrbLogger(`Command ${cmd.toString('hex')} unknown. Device ${deviceName} (${deviceId})`); + logger.verbose(`Command ${cmd.toString('hex')} unknown. Device ${deviceName} (${deviceId})`); } } wireEvents() { - infLogger('wireEvents()'); + logger.info('wireEvents()'); const self = this; this.on('pingFailed', this.onPingFailed.bind(self)); diff --git a/plejd/main.js b/plejd/main.js index a85ec70..0ba4ce5 100644 --- a/plejd/main.js +++ b/plejd/main.js @@ -1,13 +1,18 @@ const api = require('./api'); const mqtt = require('./mqtt'); const fs = require('fs'); + +const Logger = require('./Logger'); const PlejdService = require('./ble.bluez'); const SceneManager = require('./scene.manager'); +const logger = Logger.getLogger("plejd-main"); + + const version = "0.4.8"; async function main() { - console.log('starting Plejd add-on v. ' + version); + logger.info(`Starting Plejd add-on v. ${version}`); const rawData = fs.readFileSync('/data/plejd.json'); const config = JSON.parse(rawData); @@ -28,7 +33,7 @@ async function main() { const devices = plejdApi.getDevices(); client.on('connected', () => { - console.log('plejd-mqtt: connected to mqtt.'); + logger.verbose('connected to mqtt.'); client.discover(devices); }); @@ -38,7 +43,7 @@ async function main() { const sceneManager = new SceneManager(plejdApi.site, devices); const plejd = new PlejdService(cryptoKey, devices, sceneManager, config.connectionTimeout, config.writeQueueWaitTime, true); plejd.on('connectFailed', () => { - console.log('plejd-ble: were unable to connect, will retry connection in 10 seconds.'); + logger.verbose('Were unable to connect, will retry connection in 10 seconds.'); setTimeout(() => { plejd.init(); }, 10000); @@ -47,7 +52,7 @@ async function main() { plejd.init(); plejd.on('authenticated', () => { - console.log('plejd: connected via bluetooth.'); + logger.verbose('plejd: connected via bluetooth.'); }); // subscribe to changes from Plejd @@ -97,16 +102,6 @@ async function main() { plejd.turnOff(deviceId, commandObj); } }); - - client.on('settingsChanged', (settings) => { - if (settings.module === 'mqtt') { - client.updateSettings(settings); - } else if (settings.module === 'ble') { - plejd.updateSettings(settings); - } else if (settings.module === 'api') { - plejdApi.updateSettings(settings); - } - }); }); }); }); diff --git a/plejd/mqtt.js b/plejd/mqtt.js index b03e383..6e8d9e6 100644 --- a/plejd/mqtt.js +++ b/plejd/mqtt.js @@ -1,22 +1,10 @@ const EventEmitter = require('events'); const mqtt = require('mqtt'); -const _ = require('lodash'); +const Logger = require('./Logger'); const startTopic = 'hass/status'; -// #region logging -let debug = ''; - -const getLogger = () => { - const consoleLogger = msg => console.log('plejd-mqtt', msg); - if (debug === 'console') { - return consoleLogger; - } - return _.noop; -}; - -const logger = getLogger(); -// #endregion +const logger = Logger.getLogger("plejd-mqtt"); // #region discovery @@ -30,7 +18,6 @@ const getConfigPath = plug => `${getPath(plug)}/config`; const getStateTopic = plug => `${getPath(plug)}/state`; const getCommandTopic = plug => `${getPath(plug)}/set`; const getSceneEventTopic = () => `plejd/event/scene`; -const getSettingsTopic = () => `plejd/settings`; const getDiscoveryPayload = device => ({ schema: 'json', @@ -77,6 +64,7 @@ class MqttClient extends EventEmitter { } init() { + logger.info("Initializing MQTT connection for Plejd addon"); const self = this; this.client = mqtt.connect(this.mqttBroker, { @@ -85,11 +73,11 @@ class MqttClient extends EventEmitter { }); this.client.on('connect', () => { - logger('connected to MQTT.'); + logger.info('Connected to MQTT.'); this.client.subscribe(startTopic, (err) => { if (err) { - logger('error: unable to subscribe to ' + startTopic); + logger.error(`Unable to subscribe to ${startTopic}`); } self.emit('connected'); @@ -97,18 +85,14 @@ class MqttClient extends EventEmitter { this.client.subscribe(getSubscribePath(), (err) => { if (err) { - logger('error: unable to subscribe to control topics'); + logger.error('Unable to subscribe to control topics'); } }); - this.client.subscribe(getSettingsTopic(), (err) => { - if (err) { - console.log('error: could not subscribe to settings topic'); - } - }); }); this.client.on('close', () => { + logger.verbose('Warning: mqtt channel closed event, reconnecting...'); self.reconnect(); }); @@ -119,29 +103,20 @@ class MqttClient extends EventEmitter { : message.toString(); if (topic === startTopic) { - logger('home assistant has started. lets do discovery.'); + logger.info('Home Assistant has started. lets do discovery.'); self.emit('connected'); } - else if (topic === getSettingsTopic()) { - self.emit('settingsChanged', command); - } - - if (_.includes(topic, 'set')) { + else if (topic.includes('set')) { + logger.verbose(`Got mqtt command on ${topic} - ${message}`); const device = self.devices.find(x => getCommandTopic(x) === topic); self.emit('stateChanged', device, command); } + else { + logger.verbose(`Warning: Got unrecognized mqtt command on ${topic} - ${message}`); + } }); } - updateSettings(settings) { - if (settings.debug) { - debug = 'console'; - } - else { - debug = ''; - } - } - reconnect() { this.client.reconnect(); } @@ -150,13 +125,13 @@ class MqttClient extends EventEmitter { this.devices = devices; const self = this; - logger('sending discovery of ' + devices.length + ' device(s).'); + logger.debug(`Sending discovery of ${devices.length} device(s).`); devices.forEach((device) => { - logger(`sending discovery for ${device.name}`); + logger.debug(`Sending discovery for ${device.name}`); let payload = device.type === 'switch' ? getSwitchPayload(device) : getDiscoveryPayload(device); - console.log(`plejd-mqtt: discovered ${device.type} (${device.typeName}) named ${device.name} with PID ${device.id}.`); + logger.info(`Discovered ${device.type} (${device.typeName}) named ${device.name} with PID ${device.id}.`); self.deviceMap[device.id] = payload.unique_id; @@ -171,11 +146,11 @@ class MqttClient extends EventEmitter { const device = this.devices.find(x => x.id === deviceId); if (!device) { - logger('error: ' + deviceId + ' is not handled by us.'); + logger.warn(`Unknown device id ${deviceId} - not handled by us.`); return; } - logger('updating state for ' + device.name + ': ' + data.state); + logger.verbose(`Updating state for ${device.name}: ${data.state}`); let payload = null; if (device.type === 'switch') { @@ -204,6 +179,7 @@ class MqttClient extends EventEmitter { } sceneTriggered(scene) { + logger.verbose(`Scene triggered: ${scene}`); this.client.publish( getSceneEventTopic(), JSON.stringify({ scene: scene }) diff --git a/plejd/package-lock.json b/plejd/package-lock.json index 0797c72..12bcdfd 100644 --- a/plejd/package-lock.json +++ b/plejd/package-lock.json @@ -13,6 +13,16 @@ "usb": "^1.6.0" } }, + "@dabh/diagnostics": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.2.tgz", + "integrity": "sha512-+A1YivoVDNNVCdfozHSR8v/jyuuLTMXwjWuxPFlFlUapXoGc+Gj9mDlTDDfrwl7rXCl2tNZ0kE8sIBO6YOn96Q==", + "requires": { + "colorspace": "1.1.x", + "enabled": "2.0.x", + "kuler": "^2.0.0" + } + }, "@nornagon/put": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/@nornagon/put/-/put-0.0.8.tgz", @@ -52,6 +62,11 @@ "readable-stream": "^2.0.6" } }, + "async": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.0.tgz", + "integrity": "sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==" + }, "async-limiter": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", @@ -147,6 +162,51 @@ "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" }, + "color": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/color/-/color-3.0.0.tgz", + "integrity": "sha512-jCpd5+s0s0t7p3pHQKpnJ0TpQKKdleP71LWcA0aqiljpiuAkOSUFN/dyH8ZwF0hRmFlrIuRhufds1QyEP9EB+w==", + "requires": { + "color-convert": "^1.9.1", + "color-string": "^1.5.2" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + }, + "color-string": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.4.tgz", + "integrity": "sha512-57yF5yt8Xa3czSEW1jfQDE79Idk0+AkN/4KWad6tbdxUmAs3MvjxlWSWD4deYytcRfoZ9nhKyFl1kj5tBvidbw==", + "requires": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==" + }, + "colorspace": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.2.tgz", + "integrity": "sha512-vt+OoIP2d76xLhjwbBaucYlNSpPsrJWPlBTtwCpQKIu6/CSMutyzX93O/Do0qzpH3YoHEes8YEFXyZ797rEhzQ==", + "requires": { + "color": "3.0.x", + "text-hex": "1.0.x" + } + }, "commist": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/commist/-/commist-1.1.0.tgz", @@ -199,9 +259,9 @@ } }, "dbus-next": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/dbus-next/-/dbus-next-0.8.1.tgz", - "integrity": "sha512-/sgwpcnCkLQuMOTF9I95x6qvJZCbTK2RAbHwh7C60VMroSU7rphydj3ujpqiSy5yq04aKc3meZIHpCOrZ2791g==", + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/dbus-next/-/dbus-next-0.9.1.tgz", + "integrity": "sha512-HTFKjrcNcjdjNlZR+/5LlUHBw13d+ZHQA51tfn8xUzm3WYi7uWQ99M8M4bttATCth3l4G/fxu3XC97JZ5K0oSQ==", "requires": { "@nornagon/put": "0.0.8", "abstract-socket": "^2.0.0", @@ -246,9 +306,9 @@ "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=" }, "duplexer": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", - "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=" + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", + "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==" }, "duplexify": { "version": "3.7.1", @@ -261,6 +321,11 @@ "stream-shift": "^1.0.0" } }, + "enabled": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", + "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==" + }, "end-of-stream": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", @@ -383,12 +448,27 @@ "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" }, + "fast-safe-stringify": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz", + "integrity": "sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==" + }, + "fecha": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.0.tgz", + "integrity": "sha512-aN3pcx/DSmtyoovUudctc8+6Hl4T+hI9GBBHLjA76jdZl7+b1sgh5g4k+u/GL3dTy1/pnYzKp69FpJ0OicE3Wg==" + }, "file-uri-to-path": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", "optional": true }, + "fn.name": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", + "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" + }, "follow-redirects": { "version": "1.13.1", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.1.tgz", @@ -548,6 +628,11 @@ "is-windows": "^1.0.1" } }, + "is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" + }, "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -582,6 +667,11 @@ "is-unc-path": "^1.0.0" } }, + "is-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", + "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==" + }, "is-unc-path": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz", @@ -615,15 +705,27 @@ "resolved": "https://registry.npmjs.org/jspack/-/jspack-0.0.4.tgz", "integrity": "sha1-Mt01x/3LPjRWwY+7fvntC8YjgXc=" }, + "kuler": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", + "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" + }, "leven": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz", "integrity": "sha1-wuep93IJTe6dNCAq6KzORoeHVYA=" }, - "lodash": { - "version": "4.17.19", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz", - "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==" + "logform": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.2.0.tgz", + "integrity": "sha512-N0qPlqfypFx7UHNn4B3lzS/b0uLqt2hmuoa+PpuXNYgozdJYAyauF5Ky0BWVjrxDlMWiT3qN4zPq3vVAfZy7Yg==", + "requires": { + "colors": "^1.2.1", + "fast-safe-stringify": "^2.0.4", + "fecha": "^4.2.0", + "ms": "^2.1.1", + "triple-beam": "^1.3.0" + } }, "long": { "version": "4.0.0", @@ -855,6 +957,14 @@ "wrappy": "1" } }, + "one-time": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", + "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", + "requires": { + "fn.name": "1.x.x" + } + }, "ordered-read-streams": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-1.0.1.tgz", @@ -1063,6 +1173,14 @@ "simple-concat": "^1.0.0" } }, + "simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", + "requires": { + "is-arrayish": "^0.3.1" + } + }, "sleep": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/sleep/-/sleep-6.1.0.tgz", @@ -1099,6 +1217,11 @@ } } }, + "stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=" + }, "stream-combiner": { "version": "0.0.4", "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz", @@ -1195,6 +1318,11 @@ } } }, + "text-hex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", + "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==" + }, "through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", @@ -1227,6 +1355,11 @@ "is-negated-glob": "^1.0.0" } }, + "triple-beam": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz", + "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==" + }, "tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", @@ -1316,6 +1449,59 @@ "string-width": "^1.0.2 || 2" } }, + "winston": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.3.3.tgz", + "integrity": "sha512-oEXTISQnC8VlSAKf1KYSSd7J6IWuRPQqDdo8eoRNaYKLvwSb5+79Z3Yi1lrl6KDpU6/VWaxpakDAtb1oQ4n9aw==", + "requires": { + "@dabh/diagnostics": "^2.0.2", + "async": "^3.1.0", + "is-stream": "^2.0.0", + "logform": "^2.2.0", + "one-time": "^1.0.0", + "readable-stream": "^3.4.0", + "stack-trace": "0.0.x", + "triple-beam": "^1.3.0", + "winston-transport": "^4.4.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, + "winston-transport": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.4.0.tgz", + "integrity": "sha512-Lc7/p3GtqtqPBYYtS6KCN3c77/2QCev51DvcJKbkFPQNoj1sinkGwLGFDxkXY9J6p9+EPnYs+D90uwbnaiURTw==", + "requires": { + "readable-stream": "^2.3.7", + "triple-beam": "^1.2.0" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + } + } + }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", diff --git a/plejd/package.json b/plejd/package.json index 672e4ca..d590de5 100644 --- a/plejd/package.json +++ b/plejd/package.json @@ -1,13 +1,13 @@ { "dependencies": { "@abandonware/bluetooth-hci-socket": "0.5.3-3", - "axios": "^0.21.1", - "buffer-xor": "^2.0.2", - "dbus-next": "^0.8.1", + "axios": "~0.21.1", + "buffer-xor": "~2.0.2", + "dbus-next": "~0.9.1", "fs": "0.0.1-security", - "jspack": "0.0.4", - "lodash": "^4.17.19", - "mqtt": "^3.0.0", - "sleep": "^6.1.0" + "jspack": "~0.0.4", + "mqtt": "~3.0.0", + "sleep": "~6.1.0", + "winston": "~3.3.3" } } diff --git a/plejd/scene.manager.js b/plejd/scene.manager.js index e392b6a..617ce88 100644 --- a/plejd/scene.manager.js +++ b/plejd/scene.manager.js @@ -1,5 +1,4 @@ const EventEmitter = require('events'); -const _ = require('lodash'); class SceneManager extends EventEmitter { constructor(site, devices) {