Skip to content

Commit

Permalink
feat: allow and handle response timeouts for GET-type CC commands (#1505
Browse files Browse the repository at this point in the history
)
  • Loading branch information
AlCalzone authored Jan 27, 2021
1 parent 1ee6d75 commit c2429bf
Show file tree
Hide file tree
Showing 45 changed files with 1,875 additions and 1,943 deletions.
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

0 comments on commit c2429bf

Please sign in to comment.