Skip to content

Commit

Permalink
fix: add compat flag to encode Set-type commands using target CC version
Browse files Browse the repository at this point in the history
  • Loading branch information
AlCalzone committed Jun 11, 2024
1 parent 09118c9 commit 193d131
Show file tree
Hide file tree
Showing 8 changed files with 92 additions and 0 deletions.
4 changes: 4 additions & 0 deletions docs/config-files/file-format.md
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,10 @@ Without the additional integrity checks that encapsulation CCs like `CRC-16`, `S

Some devices incorrectly encode this support information though, making the checks discard otherwise correct data. To disable the checks, set `disableStrictMeasurementValidation` to `true`.

### `encodeCCsUsingTargetVersion`

Because command classes are extended in a backwards-compatible way, the Z-Wave specifications recommend encoding command classes using the version the sender supports, regardless of the receiver's version. However it has been found that some devices do not correctly parse commands from a newer version and do not react to them. When `encodeCCsUsingTargetVersion` is set to `true` for a device, Z-Wave JS will encode commands using the version the receiver supports instead.

### `forceNotificationIdleReset`

Version 8 of the `Notification CC` added the requirement that devices must issue an idle notification after a notification variable is no longer active. Several legacy devices and some misbehaving V8 devices do not return their variables to idle automatically. By setting `forceNotificationIdleReset` to `true`, `zwave-js` auto-idles supporting notification variables after 5 minutes.
Expand Down
3 changes: 3 additions & 0 deletions maintenance/schemas/device-config.json
Original file line number Diff line number Diff line change
Expand Up @@ -579,6 +579,9 @@
"disableStrictMeasurementValidation": {
"const": true
},
"encodeCCsUsingTargetVersion": {
"const": true
},
"forceNotificationIdleReset": {
"const": true
},
Expand Down
10 changes: 10 additions & 0 deletions packages/cc/src/cc/BasicCC.ts
Original file line number Diff line number Diff line change
Expand Up @@ -437,6 +437,16 @@ export class BasicCCReport extends BasicCC {
this.targetValue ?? 0xfe,
(this.duration ?? Duration.unknown()).serializeReport(),
]);

if (
this.version < 2 && this.host.getDeviceConfig?.(
this.nodeId as number,
)?.compat?.encodeCCsUsingTargetVersion
) {
// When forcing CC version 1, only send the current value
this.payload = this.payload.subarray(0, 1);
}

return super.serialize();
}

Expand Down
10 changes: 10 additions & 0 deletions packages/cc/src/cc/BinarySwitchCC.ts
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,16 @@ export class BinarySwitchCCSet extends BinarySwitchCC {
this.targetValue ? 0xff : 0x00,
(this.duration ?? Duration.default()).serializeSet(),
]);

if (
this.version < 2 && this.host.getDeviceConfig?.(
this.nodeId as number,
)?.compat?.encodeCCsUsingTargetVersion
) {
// When forcing CC version 1, only send the target value
this.payload = this.payload.subarray(0, 1);
}

return super.serialize();
}

Expand Down
20 changes: 20 additions & 0 deletions packages/cc/src/cc/ColorSwitchCC.ts
Original file line number Diff line number Diff line change
Expand Up @@ -945,6 +945,16 @@ export class ColorSwitchCCSet extends ColorSwitchCC {
this.payload[i] = (
this.duration ?? Duration.default()
).serializeSet();

if (
this.version < 2 && this.host.getDeviceConfig?.(
this.nodeId as number,
)?.compat?.encodeCCsUsingTargetVersion
) {
// When forcing CC version 1, omit the duration byte
this.payload = this.payload.subarray(0, -1);
}

return super.serialize();
}

Expand Down Expand Up @@ -1027,6 +1037,16 @@ export class ColorSwitchCCStartLevelChange extends ColorSwitchCC {
this.startLevel,
(this.duration ?? Duration.default()).serializeSet(),
]);

if (
this.version < 3 && this.host.getDeviceConfig?.(
this.nodeId as number,
)?.compat?.encodeCCsUsingTargetVersion
) {
// When forcing CC version 1 or 2, omit the duration byte
this.payload = this.payload.subarray(0, -1);
}

return super.serialize();
}

Expand Down
20 changes: 20 additions & 0 deletions packages/cc/src/cc/MultilevelSwitchCC.ts
Original file line number Diff line number Diff line change
Expand Up @@ -644,6 +644,16 @@ export class MultilevelSwitchCCSet extends MultilevelSwitchCC {
this.targetValue,
(this.duration ?? Duration.default()).serializeSet(),
]);

if (
this.version < 2 && this.host.getDeviceConfig?.(
this.nodeId as number,
)?.compat?.encodeCCsUsingTargetVersion
) {
// When forcing CC version 1, only include the target value
this.payload = this.payload.subarray(0, 1);
}

return super.serialize();
}

Expand Down Expand Up @@ -796,6 +806,16 @@ export class MultilevelSwitchCCStartLevelChange extends MultilevelSwitchCC {
this.startLevel,
(this.duration ?? Duration.default()).serializeSet(),
]);

if (
this.version < 2 && this.host.getDeviceConfig?.(
this.nodeId as number,
)?.compat?.encodeCCsUsingTargetVersion
) {
// When forcing CC version 1, omit the duration byte
this.payload = this.payload.subarray(0, -1);
}

return super.serialize();
}

Expand Down
10 changes: 10 additions & 0 deletions packages/cc/src/cc/ProtectionCC.ts
Original file line number Diff line number Diff line change
Expand Up @@ -521,6 +521,16 @@ export class ProtectionCCSet extends ProtectionCC {
this.local & 0b1111,
(this.rf ?? RFProtectionState.Unprotected) & 0b1111,
]);

if (
this.version < 2 && this.host.getDeviceConfig?.(
this.nodeId as number,
)?.compat?.encodeCCsUsingTargetVersion
) {
// When forcing CC version 1, only include the local state
this.payload = this.payload.subarray(0, 1);
}

return super.serialize();
}

Expand Down
15 changes: 15 additions & 0 deletions packages/config/src/devices/CompatConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,19 @@ compat option disableStrictMeasurementValidation must be true or omitted`,
definition.disableStrictMeasurementValidation;
}

if (definition.encodeCCsUsingTargetVersion != undefined) {
if (definition.encodeCCsUsingTargetVersion !== true) {
throwInvalidConfig(
"devices",
`config/devices/${filename}:
compat option encodeCCsUsingTargetVersion must be true or omitted`,
);
}

this.encodeCCsUsingTargetVersion =
definition.encodeCCsUsingTargetVersion;
}

if (definition.forceNotificationIdleReset != undefined) {
if (definition.forceNotificationIdleReset !== true) {
throwInvalidConfig(
Expand Down Expand Up @@ -602,6 +615,7 @@ compat option overrideQueries must be an object!`,
public readonly disableStrictEntryControlDataValidation?: boolean;
public readonly disableStrictMeasurementValidation?: boolean;
public readonly disableCallbackFunctionTypeCheck?: number[];
public readonly encodeCCsUsingTargetVersion?: boolean;
public readonly forceNotificationIdleReset?: boolean;
public readonly forceSceneControllerGroupCount?: number;
public readonly manualValueRefreshDelayMs?: number;
Expand Down Expand Up @@ -646,6 +660,7 @@ compat option overrideQueries must be an object!`,
"disableCallbackFunctionTypeCheck",
"disableStrictEntryControlDataValidation",
"disableStrictMeasurementValidation",
"encodeCCsUsingTargetVersion",
"forceNotificationIdleReset",
"forceSceneControllerGroupCount",
"manualValueRefreshDelayMs",
Expand Down

0 comments on commit 193d131

Please sign in to comment.