diff --git a/lib/statescontroller.js b/lib/statescontroller.js index b70a186b..ffa4e55e 100644 --- a/lib/statescontroller.js +++ b/lib/statescontroller.js @@ -461,6 +461,18 @@ class StatesController extends EventEmitter { catch { /* */ } } + // Drop a stashed error once its cause is resolved (e.g. a send to the device succeeded + // again), so the next occurrence is logged as a fresh error instead of silently counting. + unstashError(key) { + try { + if (this.stashedErrors.errors.hasOwnProperty(key)) { + delete this.stashedErrors.errors[key]; + this.stashedErrors.hasData = Object.keys(this.stashedErrors.errors).length > 0 || Object.keys(this.stashedErrors.unknownModels).length > 0; + } + } + catch { /* */ } + } + stashUnknownModel(model, msg) { try { if (!this.stashedErrors.unknownModels.hasOwnProperty(model)) { diff --git a/lib/zigbeecontroller.js b/lib/zigbeecontroller.js index 5531abdb..94f15e1f 100644 --- a/lib/zigbeecontroller.js +++ b/lib/zigbeecontroller.js @@ -1724,6 +1724,9 @@ class ZigbeeController extends EventEmitter { } } retry = 0; + // the send went through - a stashed send failure for this device is + // resolved, the next failure is logged as a fresh error again + this.adapter.stController?.unstashError(`send:${String(deviceId).replace('0x', '')}`); if (readKeys) { for (const readKey of readKeys) { if (readKey.converter) { @@ -1753,8 +1756,10 @@ class ZigbeeController extends EventEmitter { } else { retry = 0; + // stash key: repeated send failures to the same device (e.g. while it + // is unreachable) are counted instead of logged, until a send succeeds this.adapter.filterError(`Error ${error.code} on send command to '${this.devLabel(deviceId)}'.` + - ` Error: ${error.message}`, `Send command to ${deviceId} failed with`, error); + ` Error: ${error.message}`, `Send command to ${deviceId} failed with`, error, `send:${String(deviceId).replace('0x', '')}`); } } } while (retry > 0); diff --git a/main.js b/main.js index 7e874cff..3b154925 100644 --- a/main.js +++ b/main.js @@ -166,11 +166,24 @@ class Zigbee extends adapterCore.Adapter { } */ } - filterError(errormessage, message, error) { + // Error-level output of filterError. With a stash key, repeated identical errors + // (e.g. every failed send to an unreachable device, one per ~5 seconds) are collected + // by the states controller instead of flooding the log: the first one is logged as an + // error, the repeats only update the counter shown in the errors view of the admin + // tab and in info.lasterror. The caller clears the key once the cause is resolved. + logFilteredError(stashKey, text) { + if (stashKey && this.stController) { + this.stController.stashErrors(stashKey, `${text} - repeats are counted, not logged (errors view in the admin tab)`, true); + } else { + this.log.error(text); + } + } + + filterError(errormessage, message, error, stashKey) { if (error != null && error.code == undefined) { let em = error.stack.match(/failed \((.+?)\) at/); em = em || error.stack.match(/failed \((.+?)\)/); - this.log.error(`${message} no error code (${(em ? em[1] : 'undefined')})`); + this.logFilteredError(stashKey, `${message} no error code (${(em ? em[1] : 'undefined')})`); this.sendError(error, `${message} no error code`); if (this.debugActive) this.log.debug(`Stack trace for ${em}: ${error.stack}`); return; @@ -178,7 +191,7 @@ class Zigbee extends adapterCore.Adapter { const ecode = errorCodes[error.code]; if (ecode === undefined) { - this.log.error(errormessage); + this.logFilteredError(stashKey, errormessage); this.sendError(error, errormessage); return; } @@ -194,11 +207,11 @@ class Zigbee extends adapterCore.Adapter { this.log.warn(`${message}: Code ${error.code} (${ecode.message})`); break; case E_ERROR: - this.log.error(`${message}: Code ${error.code} (${ecode.message})`); + this.logFilteredError(stashKey, `${message}: Code ${error.code} (${ecode.message})`); this.sendError(error, `${message}: Code ${error.code} (${ecode.message})`); break; default: - this.log.error(`${message}: Code ${error.code} (malformed error)`); + this.logFilteredError(stashKey, `${message}: Code ${error.code} (malformed error)`); this.sendError(error, `${message}: Code ${error.code} (malformed error)`); break; }