Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: decouple CCs and messages from host instance, split ZWaveNode class into "mixins" #7260

Merged
merged 62 commits into from
Oct 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
03ac9e5
refactor: split ZWaveNode class into "mixins"
AlCalzone Oct 9, 2024
b56cbb7
fix: import loop
AlCalzone Oct 9, 2024
7510a38
refactor: replace some instances of IZWaveNode
AlCalzone Oct 9, 2024
be65623
refactor: replace some instances of IZWaveEndpoint
AlCalzone Oct 9, 2024
bf5f13a
refactor: autoCreate needs only the EndpointId
AlCalzone Oct 9, 2024
7cb90ea
refactor: constructors, use live binding for status machine
AlCalzone Oct 9, 2024
8983591
refactor: move security classes to "mixin"
AlCalzone Oct 9, 2024
cf13ee0
fix: broken test
AlCalzone Oct 9, 2024
a0a7584
refactor: further reduce dependency on IZWaveNode and IZWaveEndpoint
AlCalzone Oct 9, 2024
cbec3d4
refactor: more traits, less IZWaveNode
AlCalzone Oct 10, 2024
9b94e88
refactor: autosave :D
AlCalzone Oct 11, 2024
be5d871
refactor: that escalated quickly
AlCalzone Oct 11, 2024
190e7cd
fix: decoding messages manually
AlCalzone Oct 11, 2024
d6aca0c
refactor: move getDeviceConfig to CCParsing/EncodingContext
AlCalzone Oct 11, 2024
68c06a0
refactor: rename ICommandClass to CCId
AlCalzone Oct 11, 2024
3fd8006
refactor: move nodeIdType to message parsing contexts
AlCalzone Oct 11, 2024
38c1699
refactor: rename ZWaveValueHost to GetValueDB
AlCalzone Oct 13, 2024
a4400a8
chore: add codeshift to rename imports across monorepo
AlCalzone Oct 14, 2024
694a241
refactor: add ownNodeId to contexts
AlCalzone Oct 14, 2024
872e3bc
refactor: reuse applHost instance
AlCalzone Oct 14, 2024
5b23430
refactor: handle callback ID in the driver, allow undefined
AlCalzone Oct 14, 2024
6e55092
Merge branch 'master' into mixin-refactor
AlCalzone Oct 14, 2024
4c91090
fix: lint
AlCalzone Oct 14, 2024
0b398c6
fix: lint
AlCalzone Oct 14, 2024
7a2aec7
fix: infinite recursion
AlCalzone Oct 14, 2024
8882825
fix: assertSecurity logic
AlCalzone Oct 14, 2024
fe84f84
fix: node ID in mock-based S2 tests
AlCalzone Oct 14, 2024
b968b7b
fix: more tests
AlCalzone Oct 14, 2024
551b08c
fix: remove outdated configManager parameter
AlCalzone Oct 14, 2024
65bc886
refactor: change IZWaveEndpoint.getNodeUnsafe to GetEndpointNode<T>
AlCalzone Oct 14, 2024
c774a14
fix: failing test
AlCalzone Oct 14, 2024
b3981fa
refactor: pass ownNodeId where required
AlCalzone Oct 14, 2024
c08f8de
refactor: move __internalIsMockNode to CCParsingContext
AlCalzone Oct 14, 2024
99cd2ba
refactor: get rid of CC.knownVersion
AlCalzone Oct 14, 2024
d902546
refactor: remove usages of this.version in CC constructors
AlCalzone Oct 14, 2024
2b920ef
fix: tests
AlCalzone Oct 15, 2024
83fddb7
refactor: remove version property from CC instances
AlCalzone Oct 15, 2024
a317a25
fix: broken tests
AlCalzone Oct 15, 2024
3671f11
fix: lint
AlCalzone Oct 15, 2024
6437856
refactor: remove host instance from CCs
AlCalzone Oct 15, 2024
7f0342c
fix: tests
AlCalzone Oct 15, 2024
4c342cd
refactor: split up ZWaveApplicationHost more, remove unnecessary methods
AlCalzone Oct 16, 2024
dc7e12d
refactor: split up ZWaveApplicationHost even more
AlCalzone Oct 16, 2024
c280d4e
refactor: change host type of CCAPI to traits
AlCalzone Oct 16, 2024
f98c05a
refactor: rename CCAPI.applHost to just host
AlCalzone Oct 16, 2024
a33ed08
fix: make security managers required in contexts
AlCalzone Oct 16, 2024
e933ef2
fix: tests
AlCalzone Oct 16, 2024
8d89667
refactor: reorganize traits
AlCalzone Oct 16, 2024
d902697
refactor: remove GetEndpointNode trait
AlCalzone Oct 16, 2024
838723c
refactor: rename getNodeUnsafe to tryGetNode
AlCalzone Oct 16, 2024
89d795e
refactor: translateProperty[Key] takes context
AlCalzone Oct 16, 2024
ea549c4
refactor: rename toLogEntry parameter to ctx
AlCalzone Oct 16, 2024
f467c5a
refactor: remove ZWaveApplicationHost from more methods
AlCalzone Oct 16, 2024
2c5f84d
refactor: one more
AlCalzone Oct 16, 2024
c645f87
refactor: replace ZWaveApplicationHost in interview/refreshValues
AlCalzone Oct 16, 2024
2041a5d
fix: lint
AlCalzone Oct 17, 2024
a02315e
refactor: replace ZWaveApplicationHost in persistValues
AlCalzone Oct 17, 2024
c4daea5
refactor: replace ZWaveApplicationHost in mergePartialCCs
AlCalzone Oct 17, 2024
bd5aeea
refactor: rework TestingHost
AlCalzone Oct 17, 2024
4627146
refactor: nuke ZWaveApplicationHost into orbit
AlCalzone Oct 17, 2024
7e3bb68
fix: rename toLogEntry argument
AlCalzone Oct 17, 2024
72b8019
Merge branch 'master' into mixin-refactor
AlCalzone Oct 17, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
7 changes: 4 additions & 3 deletions .vscode/typescript.code-snippets
Original file line number Diff line number Diff line change
Expand Up @@ -664,7 +664,7 @@
"scope": "typescript",
"prefix": "zwccpv",
"body": [
"public persistValues(applHost: ZWaveApplicationHost): boolean {",
"public persistValues(applHost: ZWaveApplicationHost<CCNode>): boolean {",
"\tif (!super.persistValues(applHost)) return false;",
"\tconst valueDB = this.getValueDB(applHost);",
"",
Expand All @@ -682,6 +682,7 @@
"\tCommandClass,",
"\tgotDeserializationOptions,",
"\ttype CCCommandOptions,",
"\ttype CCNode,",
"\ttype CommandClassDeserializationOptions,",
"} from \"../lib/CommandClass\";",
"import {",
Expand Down Expand Up @@ -951,7 +952,7 @@
"scope": "typescript",
"prefix": "zwccinterview",
"body": [
"public async interview(applHost: ZWaveApplicationHost): Promise<void> {",
"public async interview(applHost: ZWaveApplicationHost<CCNode>): Promise<void> {",
"\tconst node = this.getNode(applHost)!;",
"\tconst endpoint = this.getEndpoint(applHost)!;",
"\tconst api = CCAPI.create(",
Expand Down Expand Up @@ -1003,7 +1004,7 @@
"scope": "typescript",
"prefix": "zwccrefval",
"body": [
"public async refreshValues(applHost: ZWaveApplicationHost): Promise<void> {",
"public async refreshValues(applHost: ZWaveApplicationHost<CCNode>): Promise<void> {",
"\tconst node = this.getNode(applHost)!;",
"\tconst endpoint = this.getEndpoint(applHost)!;",
"\tconst api = CCAPI.create(",
Expand Down
4 changes: 2 additions & 2 deletions docs/api/endpoint.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,10 @@ createCCInstanceUnsafe<T>(cc: CommandClasses): T | undefined

Like [`createCCInstance`](#createCCInstance) but returns `undefined` instead of throwing when a CC is not supported.

### `getNodeUnsafe`
### `tryGetNode`

```ts
getNodeUnsafe(): ZWaveNode | undefined
tryGetNode(): ZWaveNode | undefined
```

Returns the node this endpoint belongs to (or undefined if the node doesn't exist).
Expand Down
118 changes: 59 additions & 59 deletions packages/cc/src/cc/AlarmSensorCC.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {
CommandClasses,
type IZWaveEndpoint,
type EndpointId,
type MaybeNotKnown,
type MessageOrCCLogEntry,
MessagePriority,
Expand All @@ -11,18 +11,17 @@ import {
parseBitMask,
validatePayload,
} from "@zwave-js/core/safe";
import type {
ZWaveApplicationHost,
ZWaveHost,
ZWaveValueHost,
} from "@zwave-js/host/safe";
import type { CCEncodingContext, GetValueDB } from "@zwave-js/host/safe";
import { getEnumMemberName, isEnumMember, pick } from "@zwave-js/shared/safe";
import { validateArgs } from "@zwave-js/transformers";
import { CCAPI, PhysicalCCAPI } from "../lib/API";
import {
type CCCommandOptions,
CommandClass,
type CommandClassDeserializationOptions,
type InterviewContext,
type PersistValuesContext,
type RefreshValuesContext,
gotDeserializationOptions,
} from "../lib/CommandClass";
import {
Expand Down Expand Up @@ -129,12 +128,12 @@ export class AlarmSensorCCAPI extends PhysicalCCAPI {
public async get(sensorType?: AlarmSensorType) {
this.assertSupportsCommand(AlarmSensorCommand, AlarmSensorCommand.Get);

const cc = new AlarmSensorCCGet(this.applHost, {
const cc = new AlarmSensorCCGet({
nodeId: this.endpoint.nodeId,
endpoint: this.endpoint.index,
sensorType,
});
const response = await this.applHost.sendCommand<AlarmSensorCCReport>(
const response = await this.host.sendCommand<AlarmSensorCCReport>(
cc,
this.commandOptions,
);
Expand All @@ -148,11 +147,11 @@ export class AlarmSensorCCAPI extends PhysicalCCAPI {
AlarmSensorCommand.SupportedGet,
);

const cc = new AlarmSensorCCSupportedGet(this.applHost, {
const cc = new AlarmSensorCCSupportedGet({
nodeId: this.endpoint.nodeId,
endpoint: this.endpoint.index,
});
const response = await this.applHost.sendCommand<
const response = await this.host.sendCommand<
AlarmSensorCCSupportedReport
>(
cc,
Expand All @@ -168,38 +167,40 @@ export class AlarmSensorCCAPI extends PhysicalCCAPI {
export class AlarmSensorCC extends CommandClass {
declare ccCommand: AlarmSensorCommand;

public async interview(applHost: ZWaveApplicationHost): Promise<void> {
const node = this.getNode(applHost)!;
const endpoint = this.getEndpoint(applHost)!;
public async interview(
ctx: InterviewContext,
): Promise<void> {
const node = this.getNode(ctx)!;
const endpoint = this.getEndpoint(ctx)!;

// Skip the interview in favor of Notification CC if possible
if (endpoint.supportsCC(CommandClasses.Notification)) {
applHost.controllerLog.logNode(node.id, {
ctx.logNode(node.id, {
endpoint: this.endpointIndex,
message:
`${this.constructor.name}: skipping interview because Notification CC is supported...`,
direction: "none",
});
this.setInterviewComplete(applHost, true);
this.setInterviewComplete(ctx, true);
return;
}

const api = CCAPI.create(
CommandClasses["Alarm Sensor"],
applHost,
ctx,
endpoint,
).withOptions({
priority: MessagePriority.NodeQuery,
});

applHost.controllerLog.logNode(node.id, {
ctx.logNode(node.id, {
endpoint: this.endpointIndex,
message: `Interviewing ${this.ccName}...`,
direction: "none",
});

// Find out which sensor types this sensor supports
applHost.controllerLog.logNode(node.id, {
ctx.logNode(node.id, {
endpoint: this.endpointIndex,
message: "querying supported sensor types...",
direction: "outbound",
Expand All @@ -212,13 +213,13 @@ export class AlarmSensorCC extends CommandClass {
.map((name) => `\n· ${name}`)
.join("")
}`;
applHost.controllerLog.logNode(node.id, {
ctx.logNode(node.id, {
endpoint: this.endpointIndex,
message: logMessage,
direction: "inbound",
});
} else {
applHost.controllerLog.logNode(node.id, {
ctx.logNode(node.id, {
endpoint: this.endpointIndex,
message:
"Querying supported sensor types timed out, skipping interview...",
Expand All @@ -228,25 +229,27 @@ export class AlarmSensorCC extends CommandClass {
}

// Query (all of) the sensor's current value(s)
await this.refreshValues(applHost);
await this.refreshValues(ctx);

// Remember that the interview is complete
this.setInterviewComplete(applHost, true);
this.setInterviewComplete(ctx, true);
}

public async refreshValues(applHost: ZWaveApplicationHost): Promise<void> {
const node = this.getNode(applHost)!;
const endpoint = this.getEndpoint(applHost)!;
public async refreshValues(
ctx: RefreshValuesContext,
): Promise<void> {
const node = this.getNode(ctx)!;
const endpoint = this.getEndpoint(ctx)!;
const api = CCAPI.create(
CommandClasses["Alarm Sensor"],
applHost,
ctx,
endpoint,
).withOptions({
priority: MessagePriority.NodeQuery,
});

const supportedSensorTypes: readonly AlarmSensorType[] =
this.getValue(applHost, AlarmSensorCCValues.supportedSensorTypes)
this.getValue(ctx, AlarmSensorCCValues.supportedSensorTypes)
?? [];

// Always query (all of) the sensor's current value(s)
Expand All @@ -257,7 +260,7 @@ export class AlarmSensorCC extends CommandClass {

const sensorName = getEnumMemberName(AlarmSensorType, type);

applHost.controllerLog.logNode(node.id, {
ctx.logNode(node.id, {
endpoint: this.endpointIndex,
message: `querying current value for ${sensorName}...`,
direction: "outbound",
Expand All @@ -274,7 +277,7 @@ severity: ${currentValue.severity}`;
message += `
duration: ${currentValue.duration}`;
}
applHost.controllerLog.logNode(node.id, {
ctx.logNode(node.id, {
endpoint: this.endpointIndex,
message,
direction: "inbound",
Expand All @@ -288,10 +291,10 @@ duration: ${currentValue.duration}`;
* This only works AFTER the interview process
*/
public static getSupportedSensorTypesCached(
applHost: ZWaveApplicationHost,
endpoint: IZWaveEndpoint,
ctx: GetValueDB,
endpoint: EndpointId,
): MaybeNotKnown<AlarmSensorType[]> {
return applHost
return ctx
.getValueDB(endpoint.nodeId)
.getValue(
AlarmSensorCCValues.supportedSensorTypes.endpoint(
Expand All @@ -301,27 +304,26 @@ duration: ${currentValue.duration}`;
}

protected createMetadataForSensorType(
applHost: ZWaveApplicationHost,
ctx: GetValueDB,
sensorType: AlarmSensorType,
): void {
const stateValue = AlarmSensorCCValues.state(sensorType);
const severityValue = AlarmSensorCCValues.severity(sensorType);
const durationValue = AlarmSensorCCValues.duration(sensorType);

// Always create metadata if it does not exist
this.ensureMetadata(applHost, stateValue);
this.ensureMetadata(applHost, severityValue);
this.ensureMetadata(applHost, durationValue);
this.ensureMetadata(ctx, stateValue);
this.ensureMetadata(ctx, severityValue);
this.ensureMetadata(ctx, durationValue);
}
}

@CCCommand(AlarmSensorCommand.Report)
export class AlarmSensorCCReport extends AlarmSensorCC {
public constructor(
host: ZWaveHost,
options: CommandClassDeserializationOptions,
) {
super(host, options);
super(options);
validatePayload(this.payload.length >= 5, this.payload[1] !== 0xff);
// Alarm Sensor reports may be forwarded by a different node, in this case
// (and only then!) the payload contains the original node ID
Expand All @@ -345,7 +347,7 @@ export class AlarmSensorCCReport extends AlarmSensorCC {
public readonly severity: number | undefined;
public readonly duration: number | undefined;

public toLogEntry(host?: ZWaveValueHost): MessageOrCCLogEntry {
public toLogEntry(ctx?: GetValueDB): MessageOrCCLogEntry {
const message: MessageRecord = {
"sensor type": getEnumMemberName(AlarmSensorType, this.sensorType),
"alarm state": this.state,
Expand All @@ -357,23 +359,23 @@ export class AlarmSensorCCReport extends AlarmSensorCC {
message.duration = `${this.duration} seconds`;
}
return {
...super.toLogEntry(host),
...super.toLogEntry(ctx),
message,
};
}

public persistValues(applHost: ZWaveApplicationHost): boolean {
if (!super.persistValues(applHost)) return false;
public persistValues(ctx: PersistValuesContext): boolean {
if (!super.persistValues(ctx)) return false;
// Create metadata if it does not exist
this.createMetadataForSensorType(applHost, this.sensorType);
this.createMetadataForSensorType(ctx, this.sensorType);

const stateValue = AlarmSensorCCValues.state(this.sensorType);
const severityValue = AlarmSensorCCValues.severity(this.sensorType);
const durationValue = AlarmSensorCCValues.duration(this.sensorType);

this.setValue(applHost, stateValue, this.state);
this.setValue(applHost, severityValue, this.severity);
this.setValue(applHost, durationValue, this.duration);
this.setValue(ctx, stateValue, this.state);
this.setValue(ctx, severityValue, this.severity);
this.setValue(ctx, durationValue, this.duration);

return true;
}
Expand All @@ -399,10 +401,9 @@ export interface AlarmSensorCCGetOptions extends CCCommandOptions {
@expectedCCResponse(AlarmSensorCCReport, testResponseForAlarmSensorGet)
export class AlarmSensorCCGet extends AlarmSensorCC {
public constructor(
host: ZWaveHost,
options: CommandClassDeserializationOptions | AlarmSensorCCGetOptions,
) {
super(host, options);
super(options);
if (gotDeserializationOptions(options)) {
// TODO: Deserialize payload
throw new ZWaveError(
Expand All @@ -416,14 +417,14 @@ export class AlarmSensorCCGet extends AlarmSensorCC {

public sensorType: AlarmSensorType;

public serialize(): Buffer {
public serialize(ctx: CCEncodingContext): Buffer {
this.payload = Buffer.from([this.sensorType]);
return super.serialize();
return super.serialize(ctx);
}

public toLogEntry(host?: ZWaveValueHost): MessageOrCCLogEntry {
public toLogEntry(ctx?: GetValueDB): MessageOrCCLogEntry {
return {
...super.toLogEntry(host),
...super.toLogEntry(ctx),
message: {
"sensor type": getEnumMemberName(
AlarmSensorType,
Expand All @@ -437,10 +438,9 @@ export class AlarmSensorCCGet extends AlarmSensorCC {
@CCCommand(AlarmSensorCommand.SupportedReport)
export class AlarmSensorCCSupportedReport extends AlarmSensorCC {
public constructor(
host: ZWaveHost,
options: CommandClassDeserializationOptions,
) {
super(host, options);
super(options);
validatePayload(this.payload.length >= 1);
const bitMaskLength = this.payload[0];
validatePayload(this.payload.length >= 1 + bitMaskLength);
Expand All @@ -456,18 +456,18 @@ export class AlarmSensorCCSupportedReport extends AlarmSensorCC {
return this._supportedSensorTypes;
}

public persistValues(applHost: ZWaveApplicationHost): boolean {
if (!super.persistValues(applHost)) return false;
public persistValues(ctx: PersistValuesContext): boolean {
if (!super.persistValues(ctx)) return false;
// Create metadata for each sensor type
for (const type of this._supportedSensorTypes) {
this.createMetadataForSensorType(applHost, type);
this.createMetadataForSensorType(ctx, type);
}
return true;
}

public toLogEntry(host?: ZWaveValueHost): MessageOrCCLogEntry {
public toLogEntry(ctx?: GetValueDB): MessageOrCCLogEntry {
return {
...super.toLogEntry(host),
...super.toLogEntry(ctx),
message: {
"supported sensor types": this._supportedSensorTypes
.map((t) => getEnumMemberName(AlarmSensorType, t))
Expand Down
Loading
Loading