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

feat: allow and handle response timeouts for GET-type CC commands #1505

Merged
merged 36 commits into from
Jan 27, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
184ad77
feat(driver): do not throw when a node response times out
AlCalzone Jan 26, 2021
011af19
fix(driver): supervision might not return a response
AlCalzone Jan 26, 2021
b5a63aa
refactor: change CC APIs to include undefined in the return type
AlCalzone Jan 26, 2021
8822582
fix(cc): deal with missing reports in MCA CC
AlCalzone Jan 27, 2021
0b9b058
fix(cc): deal with missing reports in User Code CC
AlCalzone Jan 27, 2021
88c969b
fix(cc): deal with missing reports in Thermostat Setpoint CC
AlCalzone Jan 27, 2021
a8c10c5
fix(cc): deal with missing reports in Thermostat Setback CC
AlCalzone Jan 27, 2021
e1a7a93
fix(cc): deal with missing reports in Thermostat Operating State CC
AlCalzone Jan 27, 2021
7cda91e
fix(cc): deal with missing reports in Thermostat Mode CC
AlCalzone Jan 27, 2021
0e9602f
fix(cc): deal with missing reports in Sound Switch CC
AlCalzone Jan 27, 2021
a80ced6
fix(cc): deal with missing reports in Protection CC
AlCalzone Jan 27, 2021
3004505
fix(cc): deal with missing reports in Multilevel Sensor CC
AlCalzone Jan 27, 2021
fb12819
fix(cc): deal with missing reports in Meter CC
AlCalzone Jan 27, 2021
e35d3d2
fix(cc): deal with missing reports in Language CC
AlCalzone Jan 27, 2021
1aa36c3
fix(cc): deal with missing reports in Indicator CC
AlCalzone Jan 27, 2021
af290c1
fix(cc): deal with missing reports in Door Lock CC
AlCalzone Jan 27, 2021
edf70f5
fix(cc): deal with missing reports in Configuration CC
AlCalzone Jan 27, 2021
c2d216d
fix(cc): deal with missing reports in Color Switch CC
AlCalzone Jan 27, 2021
c7a38d6
fix(cc): deal with missing reports in Multilevel Switch CC
AlCalzone Jan 27, 2021
9f482ef
fix(cc): deal with missing reports in Binary Switch CC
AlCalzone Jan 27, 2021
dd8502a
fix(cc): deal with missing reports in Battery CC
AlCalzone Jan 27, 2021
bef97b4
fix(cc): deal with missing reports in Binary Sensor CC
AlCalzone Jan 27, 2021
3d98eec
fix(cc): deal with missing reports in Z-Wave+ CC
AlCalzone Jan 27, 2021
855a787
fix(cc): deal with missing reports in Wake Up CC
AlCalzone Jan 27, 2021
d86846c
fix(cc): deal with missing reports in Security CC
AlCalzone Jan 27, 2021
a619f73
fix(cc): deal with missing reports in Version CC
AlCalzone Jan 27, 2021
486f8a6
fix(cc): deal with missing reports in Notification CC
AlCalzone Jan 27, 2021
c73f5a8
fix(cc): deal with missing reports in Multi Channel CC
AlCalzone Jan 27, 2021
0a50d43
fix(cc): deal with missing reports in Manufacturer Specific CC
AlCalzone Jan 27, 2021
4285695
fix(cc): deal with missing reports in Clock CC
AlCalzone Jan 27, 2021
d845c2f
fix(cc): deal with missing reports in AGI CC
AlCalzone Jan 27, 2021
960a087
fix(cc): deal with missing reports in Basic CC
AlCalzone Jan 27, 2021
4878754
fix(cc): deal with missing reports in Central Scene CC
AlCalzone Jan 27, 2021
deb65b1
fix(cc): deal with missing reports during firmware update
AlCalzone Jan 27, 2021
faadff3
chore: cleanup
AlCalzone Jan 27, 2021
5bbe739
build: fix compile error
AlCalzone Jan 27, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 0 additions & 24 deletions packages/zwave-js/src/lib/commandclass/API.ts
Original file line number Diff line number Diff line change
Expand Up @@ -236,30 +236,6 @@ export class PhysicalCCAPI extends CCAPI {
protected declare readonly endpoint: Endpoint;
}

/**
* Executes the given action and ignores any node timeout errors
* Returns whether the execution was successful (`true`) or timed out (`false`)
*/
export async function ignoreTimeout(
action: () => Promise<void>,
onTimeout?: () => void,
): Promise<boolean> {
try {
await action();
return true;
} catch (e: unknown) {
if (
e instanceof ZWaveError &&
e.code === ZWaveErrorCodes.Controller_NodeTimeout
) {
onTimeout?.();
return false;
}
// We don't want to swallow any other errors
throw e;
}
}

// This interface is auto-generated by maintenance/generateCCAPIInterface.ts
// Do not edit it by hand or your changes will be lost
export interface CCAPIs {
Expand Down
106 changes: 46 additions & 60 deletions packages/zwave-js/src/lib/commandclass/AlarmSensorCC.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
import { getEnumMemberName, pick } from "@zwave-js/shared";
import type { Driver } from "../driver/Driver";
import { MessagePriority } from "../message/Constants";
import { ignoreTimeout, PhysicalCCAPI } from "./API";
import { PhysicalCCAPI } from "./API";
import {
API,
CCCommand,
Expand Down Expand Up @@ -125,11 +125,11 @@ export class AlarmSensorCCAPI extends PhysicalCCAPI {
endpoint: this.endpoint.index,
sensorType,
});
const response = (await this.driver.sendCommand<AlarmSensorCCReport>(
const response = await this.driver.sendCommand<AlarmSensorCCReport>(
cc,
this.commandOptions,
))!;
return pick(response, ["state", "severity", "duration"]);
);
if (response) return pick(response, ["state", "severity", "duration"]);
}

// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
Expand All @@ -143,11 +143,11 @@ export class AlarmSensorCCAPI extends PhysicalCCAPI {
nodeId: this.endpoint.nodeId,
endpoint: this.endpoint.index,
});
const response = (await this.driver.sendCommand<AlarmSensorCCSupportedReport>(
const response = await this.driver.sendCommand<AlarmSensorCCSupportedReport>(
cc,
this.commandOptions,
))!;
return response.supportedSensorTypes;
);
if (response) return response.supportedSensorTypes;
}
}

Expand Down Expand Up @@ -186,25 +186,23 @@ export class AlarmSensorCC extends CommandClass {
// Find out which sensor types this sensor supports
let supportedSensorTypes: readonly AlarmSensorType[] | undefined;
if (complete) {
if (
!(await ignoreTimeout(async () => {
this.driver.controllerLog.logNode(node.id, {
endpoint: this.endpointIndex,
message: "querying supported sensor types...",
direction: "outbound",
});
supportedSensorTypes = await api.getSupportedSensorTypes();
const logMessage = `received supported sensor types: ${supportedSensorTypes
.map((type) => getEnumMemberName(AlarmSensorType, type))
.map((name) => `\n· ${name}`)
.join("")}`;
this.driver.controllerLog.logNode(node.id, {
endpoint: this.endpointIndex,
message: logMessage,
direction: "inbound",
});
}))
) {
this.driver.controllerLog.logNode(node.id, {
endpoint: this.endpointIndex,
message: "querying supported sensor types...",
direction: "outbound",
});
supportedSensorTypes = await api.getSupportedSensorTypes();
if (supportedSensorTypes) {
const logMessage = `received supported sensor types: ${supportedSensorTypes
.map((type) => getEnumMemberName(AlarmSensorType, type))
.map((name) => `\n· ${name}`)
.join("")}`;
this.driver.controllerLog.logNode(node.id, {
endpoint: this.endpointIndex,
message: logMessage,
direction: "inbound",
});
} else {
this.driver.controllerLog.logNode(node.id, {
endpoint: this.endpointIndex,
message:
Expand All @@ -221,43 +219,31 @@ export class AlarmSensorCC extends CommandClass {
}

// Always query (all of) the sensor's current value(s)
if (supportedSensorTypes) {
for (const type of supportedSensorTypes) {
const sensorName = getEnumMemberName(AlarmSensorType, type);

await ignoreTimeout(
async () => {
this.driver.controllerLog.logNode(node.id, {
endpoint: this.endpointIndex,
message: `querying current value for ${sensorName}...`,
direction: "outbound",
});
const currentValue = await api.get(type);
let message = `received current value for ${sensorName}:
for (const type of supportedSensorTypes) {
const sensorName = getEnumMemberName(AlarmSensorType, type);

this.driver.controllerLog.logNode(node.id, {
endpoint: this.endpointIndex,
message: `querying current value for ${sensorName}...`,
direction: "outbound",
});
const currentValue = await api.get(type);
if (currentValue) {
let message = `received current value for ${sensorName}:
state: ${currentValue.state}`;
if (currentValue.severity != undefined) {
message += `
if (currentValue.severity != undefined) {
message += `
severity: ${currentValue.severity}`;
}
if (currentValue.duration != undefined) {
message += `
}
if (currentValue.duration != undefined) {
message += `
duration: ${currentValue.duration}`;
}
this.driver.controllerLog.logNode(node.id, {
endpoint: this.endpointIndex,
message,
direction: "inbound",
});
},
() => {
this.driver.controllerLog.logNode(node.id, {
endpoint: this.endpointIndex,
message:
"Current value query timed out - skipping because it is not critical...",
level: "warn",
});
},
);
}
this.driver.controllerLog.logNode(node.id, {
endpoint: this.endpointIndex,
message,
direction: "inbound",
});
}
}

Expand Down
58 changes: 36 additions & 22 deletions packages/zwave-js/src/lib/commandclass/AssociationCC.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ export class AssociationCCAPI extends PhysicalCCAPI {
* Returns the number of association groups a node supports.
* Association groups are consecutive, starting at 1.
*/
public async getGroupCount(): Promise<number> {
public async getGroupCount(): Promise<number | undefined> {
this.assertSupportsCommand(
AssociationCommand,
AssociationCommand.SupportedGroupingsGet,
Expand All @@ -138,11 +138,11 @@ export class AssociationCCAPI extends PhysicalCCAPI {
nodeId: this.endpoint.nodeId,
endpoint: this.endpoint.index,
});
const response = (await this.driver.sendCommand<AssociationCCSupportedGroupingsReport>(
const response = await this.driver.sendCommand<AssociationCCSupportedGroupingsReport>(
cc,
this.commandOptions,
))!;
return response.groupCount;
);
if (response) return response.groupCount;
}

/**
Expand All @@ -157,14 +157,16 @@ export class AssociationCCAPI extends PhysicalCCAPI {
endpoint: this.endpoint.index,
groupId,
});
const response = (await this.driver.sendCommand<AssociationCCReport>(
const response = await this.driver.sendCommand<AssociationCCReport>(
cc,
this.commandOptions,
))!;
return {
maxNodes: response.maxNodes,
nodeIds: response.nodeIds,
};
);
if (response) {
return {
maxNodes: response.maxNodes,
nodeIds: response.nodeIds,
};
}
}

/**
Expand Down Expand Up @@ -310,7 +312,7 @@ export class AssociationCC extends CommandClass {
// Even if Multi Channel Association is supported, we still need to query the number of
// normal association groups since some devices report more association groups than
// multi channel association groups
let groupCount: number;
let groupCount: number | undefined;
if (complete) {
// First find out how many groups are supported
this.driver.controllerLog.logNode(node.id, {
Expand All @@ -319,11 +321,21 @@ export class AssociationCC extends CommandClass {
direction: "outbound",
});
groupCount = await api.getGroupCount();
this.driver.controllerLog.logNode(node.id, {
endpoint: this.endpointIndex,
message: `supports ${groupCount} association groups`,
direction: "inbound",
});
if (groupCount != undefined) {
this.driver.controllerLog.logNode(node.id, {
endpoint: this.endpointIndex,
message: `supports ${groupCount} association groups`,
direction: "inbound",
});
} else {
this.driver.controllerLog.logNode(node.id, {
endpoint: this.endpointIndex,
message:
"Querying association groups timed out, skipping interview...",
level: "warn",
});
return;
}
} else {
// Partial interview, read the information from cache
groupCount = this.getGroupCountCached();
Expand All @@ -350,14 +362,16 @@ export class AssociationCC extends CommandClass {
direction: "outbound",
});
const group = await api.getGroup(groupId);
const logMessage = `received information for association group #${groupId}:
if (group != undefined) {
const logMessage = `received information for association group #${groupId}:
maximum # of nodes: ${group.maxNodes}
currently assigned nodes: ${group.nodeIds.map(String).join(", ")}`;
this.driver.controllerLog.logNode(node.id, {
endpoint: this.endpointIndex,
message: logMessage,
direction: "inbound",
});
this.driver.controllerLog.logNode(node.id, {
endpoint: this.endpointIndex,
message: logMessage,
direction: "inbound",
});
}
}

// Assign the controller to all lifeline groups
Expand Down
Loading