From 987530bf1243d9b2902191c3da4d24dd3e2c3cec Mon Sep 17 00:00:00 2001 From: Dominic Griesel Date: Thu, 11 Apr 2024 13:40:35 +0200 Subject: [PATCH 1/7] fix: do not mark Basic CC as controlled when receiving Basic Report --- packages/zwave-js/src/lib/node/Node.ts | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/packages/zwave-js/src/lib/node/Node.ts b/packages/zwave-js/src/lib/node/Node.ts index f4da098cf588..c74a8d7f5bf5 100644 --- a/packages/zwave-js/src/lib/node/Node.ts +++ b/packages/zwave-js/src/lib/node/Node.ts @@ -3662,11 +3662,7 @@ protocol version: ${this.protocolVersion}`; // Since the node sent us a Basic report, we are sure that it is at least supported // If this is the only supported actuator CC, add it to the support list, // so the information lands in the network cache - if (!actuatorCCs.some((cc) => sourceEndpoint.supportsCC(cc))) { - sourceEndpoint.addCC(CommandClasses.Basic, { - isControlled: true, - }); - } + sourceEndpoint.maybeAddBasicCCAsFallback(); } } else if (command instanceof BasicCCSet) { // Treat BasicCCSet as value events if desired @@ -3708,7 +3704,7 @@ protocol version: ${this.protocolVersion}`; ), command.targetValue, ); - // Since the node sent us a Basic command, we are sure that it is at least controlled + // Since the node sent us a Basic Set, we are sure that it is at least controlled // Add it to the support list, so the information lands in the network cache if (!sourceEndpoint.controlsCC(CommandClasses.Basic)) { sourceEndpoint.addCC(CommandClasses.Basic, { From 270d4a1b72325c0934beeecea59a4d30b49f98a9 Mon Sep 17 00:00:00 2001 From: Dominic Griesel Date: Thu, 11 Apr 2024 14:00:42 +0200 Subject: [PATCH 2/7] fix: ignore force-removed CCs --- packages/zwave-js/src/lib/node/Endpoint.ts | 13 +++++++++++++ packages/zwave-js/src/lib/node/Node.ts | 15 +++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/packages/zwave-js/src/lib/node/Endpoint.ts b/packages/zwave-js/src/lib/node/Endpoint.ts index ddacfecdc171..6aa3c4d0e5c0 100644 --- a/packages/zwave-js/src/lib/node/Endpoint.ts +++ b/packages/zwave-js/src/lib/node/Endpoint.ts @@ -279,6 +279,19 @@ export class Endpoint implements IZWaveEndpoint { } } + /** Determines if support for a CC was force-removed via config file */ + public wasCCRemovedViaConfig(cc: CommandClasses): boolean { + if (this.supportsCC(cc)) return false; + + const compatConfig = this.getNodeUnsafe()?.deviceConfig?.compat; + if (!compatConfig) return false; + + const removedEndpoints = compatConfig.removeCCs?.get(cc); + if (!removedEndpoints) return false; + + return removedEndpoints == "*" || removedEndpoints.includes(this.index); + } + /** * Retrieves the version of the given CommandClass this endpoint implements. * Returns 0 if the CC is not supported. diff --git a/packages/zwave-js/src/lib/node/Node.ts b/packages/zwave-js/src/lib/node/Node.ts index c74a8d7f5bf5..03d39f52bedf 100644 --- a/packages/zwave-js/src/lib/node/Node.ts +++ b/packages/zwave-js/src/lib/node/Node.ts @@ -3042,6 +3042,21 @@ protocol version: ${this.protocolVersion}`; this.markAsAwake(); } + // If the received CC was force-removed via config file, ignore it completely + const endpoint = this.getEndpoint(command.endpointIndex); + if (endpoint?.wasCCRemovedViaConfig(command.ccId)) { + this.driver.controllerLog.logNode( + this.id, + { + endpoint: endpoint.index, + direction: "inbound", + message: + `Ignoring ${command.constructor.name} because CC support was removed via config file`, + }, + ); + return; + } + if (command instanceof BasicCC) { return this.handleBasicCommand(command); } else if (command instanceof MultilevelSwitchCC) { From 4d4352c8dc16c83c2b284af346aabd2c447d3a59 Mon Sep 17 00:00:00 2001 From: Dominic Griesel Date: Thu, 11 Apr 2024 14:10:32 +0200 Subject: [PATCH 3/7] fix: do not persist values for force-removed CCs --- packages/zwave-js/src/lib/driver/Driver.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/zwave-js/src/lib/driver/Driver.ts b/packages/zwave-js/src/lib/driver/Driver.ts index bf8b31813f6d..cb5def4db9ac 100644 --- a/packages/zwave-js/src/lib/driver/Driver.ts +++ b/packages/zwave-js/src/lib/driver/Driver.ts @@ -4712,6 +4712,10 @@ ${handlers.length} left`, /** Persists the values contained in a Command Class in the corresponding nodes's value DB */ private persistCCValues(cc: CommandClass) { + // Do not persist values for a CC that was force-removed via config + const endpoint = this.tryGetEndpoint(cc); + if (endpoint?.wasCCRemovedViaConfig(cc.ccId)) return; + cc.persistValues(this); if (isEncapsulatingCommandClass(cc)) { this.persistCCValues(cc.encapsulated); From 09665484afbfe61d61c46d1aaf56cfc1be5e1129 Mon Sep 17 00:00:00 2001 From: Dominic Griesel Date: Thu, 11 Apr 2024 14:34:14 +0200 Subject: [PATCH 4/7] fix: do not persist values on the root when CC is mapped to an endpoint --- packages/zwave-js/src/lib/driver/Driver.ts | 32 ++++++++++++++++++++-- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/packages/zwave-js/src/lib/driver/Driver.ts b/packages/zwave-js/src/lib/driver/Driver.ts index cb5def4db9ac..e3ad402bf72f 100644 --- a/packages/zwave-js/src/lib/driver/Driver.ts +++ b/packages/zwave-js/src/lib/driver/Driver.ts @@ -4710,11 +4710,37 @@ ${handlers.length} left`, } } + private shouldPersistCCValues(cc: CommandClass): boolean { + // Do not persist values for a node or endpoint that does not exist + const endpoint = this.tryGetEndpoint(cc); + const node = endpoint?.getNodeUnsafe(); + if (!node) return false; + + // Do not persist values for a CC that was force-removed via config + if (endpoint?.wasCCRemovedViaConfig(cc.ccId)) return false; + + // Do not persist values for a CC that's being mapped to another endpoint. + // FIXME: This duplicates logic in Node.ts -> handleCommand + const compatConfig = node?.deviceConfig?.compat; + if ( + cc.endpointIndex === 0 + && cc.constructor.name.endsWith("Report") + && node.getEndpointCount() >= 1 + // Only map reports from the root device to an endpoint if we know which one + && compatConfig?.mapRootReportsToEndpoint != undefined + ) { + const targetEndpoint = node.getEndpoint( + compatConfig.mapRootReportsToEndpoint, + ); + if (targetEndpoint?.supportsCC(cc.ccId)) return false; + } + + return true; + } + /** Persists the values contained in a Command Class in the corresponding nodes's value DB */ private persistCCValues(cc: CommandClass) { - // Do not persist values for a CC that was force-removed via config - const endpoint = this.tryGetEndpoint(cc); - if (endpoint?.wasCCRemovedViaConfig(cc.ccId)) return; + if (!this.shouldPersistCCValues(cc)) return; cc.persistValues(this); if (isEncapsulatingCommandClass(cc)) { From 18975dc22efa5d731a27d7276980c1f0c03d6ec2 Mon Sep 17 00:00:00 2001 From: Dominic Griesel Date: Thu, 11 Apr 2024 14:43:19 +0200 Subject: [PATCH 5/7] fix: logic for persisting values --- packages/zwave-js/src/lib/driver/Driver.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/zwave-js/src/lib/driver/Driver.ts b/packages/zwave-js/src/lib/driver/Driver.ts index e3ad402bf72f..78ec35474af6 100644 --- a/packages/zwave-js/src/lib/driver/Driver.ts +++ b/packages/zwave-js/src/lib/driver/Driver.ts @@ -4740,9 +4740,10 @@ ${handlers.length} left`, /** Persists the values contained in a Command Class in the corresponding nodes's value DB */ private persistCCValues(cc: CommandClass) { - if (!this.shouldPersistCCValues(cc)) return; + if (this.shouldPersistCCValues(cc)) { + cc.persistValues(this); + } - cc.persistValues(this); if (isEncapsulatingCommandClass(cc)) { this.persistCCValues(cc.encapsulated); } else if (isMultiEncapsulatingCommandClass(cc)) { From 99c732c99aba2d1073796e085fdae87f3bc43fd6 Mon Sep 17 00:00:00 2001 From: Dominic Griesel Date: Thu, 11 Apr 2024 15:04:15 +0200 Subject: [PATCH 6/7] fix: always persist encapsulation CCs --- packages/zwave-js/src/lib/driver/Driver.ts | 4 ++++ .../driver/receiveApplicationCommandHandlerBridge.test.ts | 6 ++++++ .../zwave-js/src/lib/test/driver/unresponsiveStick.test.ts | 4 ++-- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/packages/zwave-js/src/lib/driver/Driver.ts b/packages/zwave-js/src/lib/driver/Driver.ts index 78ec35474af6..23e5192fca1e 100644 --- a/packages/zwave-js/src/lib/driver/Driver.ts +++ b/packages/zwave-js/src/lib/driver/Driver.ts @@ -96,6 +96,7 @@ import { serializeCacheValue, stripUndefined, timespan, + isEncapsulationCC, } from "@zwave-js/core"; import type { NodeSchedulePollOptions, @@ -4711,6 +4712,9 @@ ${handlers.length} left`, } private shouldPersistCCValues(cc: CommandClass): boolean { + // Always persist encapsulation CCs, otherwise interviews don't work. + if (isEncapsulationCC(cc.ccId)) return true; + // Do not persist values for a node or endpoint that does not exist const endpoint = this.tryGetEndpoint(cc); const node = endpoint?.getNodeUnsafe(); diff --git a/packages/zwave-js/src/lib/test/driver/receiveApplicationCommandHandlerBridge.test.ts b/packages/zwave-js/src/lib/test/driver/receiveApplicationCommandHandlerBridge.test.ts index d75c220776e3..a500c977566a 100644 --- a/packages/zwave-js/src/lib/test/driver/receiveApplicationCommandHandlerBridge.test.ts +++ b/packages/zwave-js/src/lib/test/driver/receiveApplicationCommandHandlerBridge.test.ts @@ -39,6 +39,12 @@ test.beforeEach(async (t) => { removeAllListeners: () => {}, } as any; + driver.controller.nodes.getOrThrow = (nodeId: number) => { + const node = driver.controller.nodes.get(nodeId); + if (!node) throw new Error(`Node ${nodeId} not found`); + return node; + }; + t.context = { driver, serialport }; }); diff --git a/packages/zwave-js/src/lib/test/driver/unresponsiveStick.test.ts b/packages/zwave-js/src/lib/test/driver/unresponsiveStick.test.ts index ad9729777de1..340e6b19c18a 100644 --- a/packages/zwave-js/src/lib/test/driver/unresponsiveStick.test.ts +++ b/packages/zwave-js/src/lib/test/driver/unresponsiveStick.test.ts @@ -128,10 +128,10 @@ integrationTest( }, ); -integrationTest.only( +integrationTest( "The unresponsive controller recovery does not kick in when it was enabled via config", { - debug: true, + // debug: true, additionalDriverOptions: { attempts: { From c6e0c719fe6ca6c7d3a1dcd355ee57c08fd55dcf Mon Sep 17 00:00:00 2001 From: Dominic Griesel Date: Thu, 11 Apr 2024 15:16:21 +0200 Subject: [PATCH 7/7] fix: lint --- packages/zwave-js/src/lib/driver/Driver.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/zwave-js/src/lib/driver/Driver.ts b/packages/zwave-js/src/lib/driver/Driver.ts index 23e5192fca1e..d217d4a294f6 100644 --- a/packages/zwave-js/src/lib/driver/Driver.ts +++ b/packages/zwave-js/src/lib/driver/Driver.ts @@ -86,6 +86,7 @@ import { deserializeCacheValue, getCCName, highResTimestamp, + isEncapsulationCC, isLongRangeNodeId, isMissingControllerACK, isMissingControllerCallback, @@ -96,7 +97,6 @@ import { serializeCacheValue, stripUndefined, timespan, - isEncapsulationCC, } from "@zwave-js/core"; import type { NodeSchedulePollOptions,