-
-
Notifications
You must be signed in to change notification settings - Fork 633
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor: decouple CCs and messages from host, split parsing and crea…
…tion, split ZWaveNode class (#7305)
- Loading branch information
Showing
387 changed files
with
34,775 additions
and
25,800 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,195 @@ | ||
# Migrating to v14 <!-- {docsify-ignore-all} --> | ||
|
||
This version became necessary more or less by accident. I originally planned to split some of the files that have grown too large (like `Node.ts`) into multiple smaller chunks. During this refactor, I noticed that several methods depended on a full-blown driver instance (or an appropriate abstraction), when all that was needed was access to the value DB for example. This problem appeared throughout the entire codebase, so I decided to do something about it. | ||
|
||
The `ZWaveHost` and `ZWaveApplicationHost` interfaces have been replaced by multiple smaller interfaces, each defining a specific subset of the functionality. All the code that previously expected to be passed one of those interfaces has been update to just accept what it actually needs. The `Driver` class still implements all of this functionality. | ||
|
||
Furthermore, `Message` and `CommandClass` implementations are no longer bound to a specific host instance. Instead, their methods that need access to host functionality (like value DBs, home ID, device configuration, etc.) now receive a method-specific context object. Parsing of those instances no longer happens in the constructor, but in a separate `from` method. | ||
|
||
All in all, this release contains a huge list of breaking changes, but most of those should not affect any application, unless very low-level APIs are frequently used. | ||
|
||
## Decoupled Serial API messages from host instances, split constructors and parsing | ||
|
||
`Message` instances no longer store a reference to their host. They are now "just data". Therefore, message constructors no longer take an instance of `ZWaveHost` as the first parameter. All needed information is passed to the relevant methods as context arguments. | ||
|
||
**Old: constructor for creation and parsing** | ||
|
||
```ts | ||
public constructor( | ||
options: | ||
| MessageDeserializationOptions | ||
| MyMessageOptions, | ||
) { | ||
super(options); | ||
|
||
if (gotDeserializationOptions(options)) { | ||
// deserialize message from this.payload | ||
} else { | ||
// populate message properties from options | ||
} | ||
} | ||
``` | ||
|
||
**New: constructor for creation, separate method for parsing** | ||
|
||
```ts | ||
public constructor( | ||
options: MyMessageOptions & MessageBaseOptions, | ||
) { | ||
super(options); | ||
// populate message properties from options | ||
} | ||
|
||
public static from( | ||
raw: MessageRaw, | ||
ctx: MessageParsingContext, | ||
): MyMessage { | ||
// deserialize message from raw.payload | ||
|
||
return new this({ | ||
// ... | ||
}); | ||
} | ||
``` | ||
|
||
> [!NOTE]: For messages that contain a serialized CC instance, Message.parse no longer deserializes it automatically. This has to be done in a separate step. | ||
## Decoupled CCs from host instances, split constructors and parsing | ||
|
||
`CommandClass` instances no longer store a reference to their host. They are now "just data". Therefore, CC constructors no longer take an instance of `ZWaveHost` as the first parameter. All needed information is passed to the relevant methods as context arguments. | ||
|
||
**Old: constructor for creation and parsing** | ||
|
||
```ts | ||
public constructor( | ||
host: ZWaveHost, | ||
options: CommandClassDeserializationOptions | BinarySwitchCCSetOptions, | ||
) { | ||
super(host, options); | ||
if (gotDeserializationOptions(options)) { | ||
validatePayload(this.payload.length >= 1); | ||
this.targetValue = !!this.payload[0]; | ||
if (this.payload.length >= 2) { | ||
this.duration = Duration.parseSet(this.payload[1]); | ||
} | ||
} else { | ||
this.targetValue = options.targetValue; | ||
this.duration = Duration.from(options.duration); | ||
} | ||
} | ||
``` | ||
|
||
**New: constructor for creation, separate method for parsing** | ||
|
||
```ts | ||
public constructor( | ||
options: WithAddress<BinarySwitchCCSetOptions>, | ||
) { | ||
super(options); | ||
this.targetValue = options.targetValue; | ||
this.duration = Duration.from(options.duration); | ||
} | ||
|
||
public static from(raw: CCRaw, ctx: CCParsingContext): BinarySwitchCCSet { | ||
validatePayload(raw.payload.length >= 1); | ||
const targetValue = !!raw.payload[0]; | ||
|
||
let duration: Duration | undefined; | ||
if (raw.payload.length >= 2) { | ||
duration = Duration.parseSet(raw.payload[1]); | ||
} | ||
|
||
return new BinarySwitchCCSet({ | ||
nodeId: ctx.sourceNodeId, | ||
targetValue, | ||
duration, | ||
}); | ||
} | ||
``` | ||
|
||
**Updated CC method signatures:** | ||
|
||
```diff | ||
- interview(applHost: ZWaveApplicationHost): Promise<void>; | ||
+ interview(ctx: InterviewContext): Promise<void>; | ||
|
||
- refreshValues(applHost: ZWaveApplicationHost): Promise<void>; | ||
+ refreshValues(ctx: RefreshValuesContext): Promise<void>; | ||
|
||
- persistValues(applHost: ZWaveApplicationHost): Promise<void>; | ||
+ persistValues(ctx: PersistValuesContext): Promise<void>; | ||
|
||
- toLogEntry(host?: ZWaveValueHost): MessageOrCCLogEntry; | ||
+ toLogEntry(ctx?: GetValueDB): MessageOrCCLogEntry; | ||
``` | ||
|
||
> [!NOTE]: Applications calling these methods can simply pass the driver instance. | ||
## Moved Serial API message implementations to `@zwave-js/serial` package | ||
|
||
All serial API message implementations have been moved to the `@zwave-js/serial` package. In addition to the main entry point, they are also available via `@zwave-js/serial/serialapi`. | ||
|
||
## Other notable changes | ||
|
||
These are the only ones I found to break dependent projects. | ||
|
||
- All `getNodeUnsafe` methods have been renamed to `tryGetNode` to better indicate what they do - it was not really clear what was "unsafe" about them. | ||
- The `CommandClass.version` property has been removed. To determine the CC version a node or endpoint supports, use the `node.getCCVersion(cc)` or `endpoint.getCCVersion(cc)` methods. | ||
- The `TXReport` object no longer has the `numRepeaters` property, as this was implied by the length of the `repeaterNodeIds` array. | ||
|
||
## Other breaking changes | ||
|
||
Most of these should not affect any application: | ||
|
||
- The `ZWaveHost` and `ZWaveApplicationHost` interfaces have been split into multiple smaller "traits" | ||
- The `Driver` class still implements all those traits, but receiving functions now just require the necessary subset. | ||
- CC Constructors no longer take an instance of `ZWaveHost` as the first parameter. All needed information has been merged into the `CCParsingContext` type. | ||
- The `CommandClass.from` method no longer takes an instance of `ZWaveHost` as the first parameter for the same reason. | ||
- `CommandClass` instances no longer store a reference to their host. They are now "just data". | ||
- The `serialize()` implementations of CCs now take an argument of type `CCEncodingContext` | ||
- The `toLogEntry()` implementations of CCs now take an argument of type `GetValueDB` instead of `ZWaveHost` | ||
- The `interview()` implementations of CCs now take an argument of type `InterviewContext` instead of `ZWaveApplicationHost` | ||
- The `refreshValues()` implementations of CCs now take an argument of type `RefreshValuesContext` instead of `ZWaveApplicationHost` | ||
- The `persistValues()` implementations of CCs now take an argument of type `PersistValuesContext` instead of `ZWaveApplicationHost` | ||
- The `translateProperty()` and `translatePropertyKey()` implementations of CCs now take an argument of type `GetValueDB` instead of `ZWaveApplicationHost` | ||
- The `mergePartialCCs` signature has changed: | ||
```diff | ||
- mergePartialCCs(applHost: ZWaveApplicationHost, partials: CommandClass[]): void; | ||
+ mergePartialCCs(partials: CommandClass[], ctx: CCParsingContext): void; | ||
``` | ||
- The `ICommandClass` interface has been removed. Where applicable, it has been replaced with the `CCId` interface whose sole purpose is to identify the command and destination of a CC. | ||
- The `IZWaveNode`, `IZWaveEndpoint`, `IVirtualNode` and `IVirtualEndpoint` interfaces have been replaced with more specific "trait" interfaces. | ||
- The `TestingHost`, `TestNode` and `TestEndpoint` interfaces and implementations have been reworked to be more declarative and require less input in tests. | ||
- Serial API `Message` constructors no longer take an instance of `ZWaveHost` as the first parameter. All needed information has been merged into the `MessageParsingContext` type. | ||
- The `Message.from` method no longer takes an instance of `ZWaveHost` as the first parameter for the same reason. | ||
- The `callbackId` of a `Message` instance is no longer automatically generated when reading it for the first time. Instead, the `callbackId` can be `undefined` and has to be explicitly set before serializing if necessary. | ||
- The `serialize()` implementations of `Message`s now take an argument of type `MessageEncodingContext` | ||
- The `Message.getNodeUnsafe()` method has been renamed to `Message.tryGetNode` | ||
- The `Driver.getNodeUnsafe(message)` method has been renamed to `Driver.tryGetNode(message)` | ||
- `Driver.controllerLog` is no longer public. | ||
- `Driver.getSafeCCVersion` can now return `undefined` if the requested CC is not implemented in `zwave-js` | ||
- `Driver.isControllerNode(nodeId)` has been removed. Compare with `ownNodeId` instead or use the `Node.isControllerNode` property. | ||
- The `Endpoint.getNodeUnsafe()` method has been renamed to `tryGetNode` | ||
- The `[CCName]CC[CCCommand]Options` interfaces no longer extend another interface and now exclusively contain the CC-specific properties required for constructing that particular CC | ||
- CC-specific constructors now accept a single options object of type `WithAddress<...>`, which is the CC-specific options interface, extended by `nodeId` and `endpointIndex`. Note that `endpointIndex` was previously just called `endpoint`. | ||
- The `CommandClassDeserializationOptions` interface and the `gotDeserializationOptions` method no longer exist. Instead, CCs parse their specific payload using the new static `from` method, which takes a pre-parsed `CCRaw` (ccId, ccCommand, binary payload) and the `CCParsingContext`. All CCs with a custom constructor implementations are expected to implement this aswell. | ||
To parse an arbitrary CC buffer, use `CommandClass.parse(buffer, context)`. | ||
- Several CCs had their constructor options type reworked slightly for correctness. In some cases it is now necessary to pass properties that were previously inferred. | ||
- The `CommandClassCreationOptions` type has been merged into the `CommandClassOptions` type, which now consists only of the CC destination and optional overrides for ccId, ccCommand and payload. | ||
- `CommandClass.deserialize` has been removed. It's functionality is now provided by the `parse` method and `CCRaw`. | ||
- `CommandClass.getCommandClass`, `CommandClass.getCCCommand` have been removed. Their functionality is now provided by `CCRaw.parse` | ||
- `CommandClass.getConstructor` has been removed. If really needed, the functionality is available via the `getCCConstructor` and `getCCCommandConstructor` methods exported by `@zwave-js/cc`. | ||
- The `origin` property of the `CommandClass` class was removed, since it served no real purpose. | ||
- The `[MessageName]Options` interfaces no longer extend another interface and now exclusively contain the message-specific properties required for constructing that particular instance | ||
- Message-specific constructors now accept a single options object of type `[MessageName]Options & MessageBaseOptions`. | ||
- The `MessageDeserializationOptions` interface and the `gotDeserializationOptions` method no longer exist | ||
- The `DeserializingMessageConstructor` interface no longer exists | ||
- Instead, message instances parse their specific payload using the new static `from` method, which takes a pre-parsed `MessageRaw` (message type, function type, binary payload) and the `MessageParsingContext`. All Message implementations with a custom constructor are expected to implement the `from` method aswell. | ||
To parse an arbitrary message buffer use `Message.parse(buffer, context)`. | ||
- The `MessageCreationOptions` type has been renamed to `MessageOptions` | ||
- Several Message implementations had their constructor options type reworked slightly for correctness. In some cases it is now necessary to pass properties that were previously inferred. | ||
- The static `Message` methods `extractPayload`, `getConstructor`, `getMessageLength` and `isComplete` were removed. | ||
- The `Message.options` property was removed | ||
- The `ICommandClassContainer` interface was replaced with the new `ContainsCC` interface, which indicates that something contains a deserialized CC instance. | ||
- The `Driver.computeNetCCPayloadSize` method now requires that the passed message instance contains a deserialized CC instance. | ||
- The `INodeQuery` interface and the `isNodeQuery` function were removed. To test whether a message references a node, use `hasNodeId(msg)` instead, which communicates the intent more clearly. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.