From 162649056b197979fe6450aafd4dc8f0f6f93837 Mon Sep 17 00:00:00 2001 From: AlCalzone Date: Sat, 30 Jan 2021 00:45:15 +0100 Subject: [PATCH] fix: treat no error as successful command, update currentValue immediately (#1522) --- packages/zwave-js/src/lib/commandclass/API.ts | 49 +++++++++++-- .../zwave-js/src/lib/commandclass/BasicCC.ts | 40 ++++------- .../src/lib/commandclass/BinarySwitchCC.ts | 42 +++++------ .../src/lib/commandclass/CentralSceneCC.ts | 5 -- .../commandclass/ClimateControlScheduleCC.ts | 10 --- .../zwave-js/src/lib/commandclass/ClockCC.ts | 5 -- .../src/lib/commandclass/ColorSwitchCC.ts | 13 +++- .../src/lib/commandclass/ConfigurationCC.ts | 6 +- .../src/lib/commandclass/DoorLockCC.ts | 21 ++---- .../src/lib/commandclass/IndicatorCC.ts | 9 --- .../src/lib/commandclass/LanguageCC.ts | 5 -- .../zwave-js/src/lib/commandclass/LockCC.ts | 6 +- .../commandclass/ManufacturerProprietaryCC.ts | 5 +- .../lib/commandclass/MultilevelSwitchCC.ts | 70 ++++++++----------- .../src/lib/commandclass/NodeNamingCC.ts | 5 +- .../src/lib/commandclass/ProtectionCC.ts | 15 ---- .../src/lib/commandclass/SoundSwitchCC.ts | 19 ++--- .../src/lib/commandclass/ThermostatModeCC.ts | 10 +-- .../lib/commandclass/ThermostatSetbackCC.ts | 5 -- .../lib/commandclass/ThermostatSetpointCC.ts | 10 +-- .../zwave-js/src/lib/commandclass/TimeCC.ts | 5 -- .../src/lib/commandclass/TimeParametersCC.ts | 5 -- .../src/lib/commandclass/UserCodeCC.ts | 15 +--- .../zwave-js/src/lib/commandclass/WakeUpCC.ts | 10 +-- packages/zwave-js/src/lib/node/Node.ts | 3 + 25 files changed, 159 insertions(+), 229 deletions(-) diff --git a/packages/zwave-js/src/lib/commandclass/API.ts b/packages/zwave-js/src/lib/commandclass/API.ts index cf809e2e76b7..6ef92839df45 100644 --- a/packages/zwave-js/src/lib/commandclass/API.ts +++ b/packages/zwave-js/src/lib/commandclass/API.ts @@ -7,24 +7,26 @@ import { ZWaveError, ZWaveErrorCodes, } from "@zwave-js/core"; -import { getEnumMemberName } from "@zwave-js/shared"; +import { getEnumMemberName, ObjectKeyMap } from "@zwave-js/shared"; import { isArray } from "alcalzone-shared/typeguards"; import type { Driver, SendCommandOptions } from "../driver/Driver"; import type { Endpoint } from "../node/Endpoint"; import { VirtualEndpoint } from "../node/VirtualEndpoint"; import { getCommandClass } from "./CommandClass"; +export type ValueIDProperties = Pick; + /** Used to identify the method on the CC API class that handles setting values on nodes directly */ export const SET_VALUE: unique symbol = Symbol.for("CCAPI_SET_VALUE"); export type SetValueImplementation = ( - property: Pick, + property: ValueIDProperties, value: unknown, ) => Promise; /** Used to identify the method on the CC API class that handles polling values from nodes */ export const POLL_VALUE: unique symbol = Symbol.for("CCAPI_POLL_VALUE"); export type PollValueImplementation = ( - property: Pick, + property: ValueIDProperties, ) => Promise; // Since the setValue API is called from a point with very generic parameters, @@ -82,6 +84,7 @@ export class CCAPI { protected readonly endpoint: Endpoint | VirtualEndpoint, ) { this.ccId = getCommandClass(this); + this.scheduledPolls = new ObjectKeyMap(); } /** @@ -105,7 +108,45 @@ export class CCAPI { */ public get pollValue(): PollValueImplementation | undefined { // wotan-disable-next-line no-restricted-property-access - return this[POLL_VALUE]; + const implementation = this[POLL_VALUE]?.bind(this); + if (!implementation) return; + // Polling manually should cancel scheduled polls to avoid polling too often + // Therefore return a wrapper which takes care of that + return (property) => { + // Cancel any scheduled polls + if (this.scheduledPolls.has(property)) { + clearTimeout(this.scheduledPolls.get(property)!); + this.scheduledPolls.delete(property); + } + // Call the implementation + return implementation(property); + }; + } + + protected scheduledPolls: ObjectKeyMap; + /** + * Schedules a value to be polled after a given time. Schedules are deduplicated on a per-property basis. + * @returns `true` if the poll was scheduled, `false` otherwise + */ + protected schedulePoll( + property: ValueIDProperties, + timeoutMs: number = this.driver.options.timeouts.refreshValue, + ): boolean { + if (this.scheduledPolls.has(property)) return false; + // wotan-disable-next-line no-restricted-property-access + if (!this[POLL_VALUE]) return false; + + this.scheduledPolls.set( + property, + setTimeout(async () => { + try { + await this.pollValue!(property); + } catch { + /* ignore */ + } + }, timeoutMs).unref(), + ); + return true; } /** diff --git a/packages/zwave-js/src/lib/commandclass/BasicCC.ts b/packages/zwave-js/src/lib/commandclass/BasicCC.ts index c350e990c6c9..4f47e6b67959 100644 --- a/packages/zwave-js/src/lib/commandclass/BasicCC.ts +++ b/packages/zwave-js/src/lib/commandclass/BasicCC.ts @@ -89,8 +89,23 @@ export class BasicCCAPI extends CCAPI { throwWrongValueType(this.ccId, property, "number", typeof value); } await this.set(value); + + // If the command did not fail, assume that it succeeded and update the currentValue accordingly + // so UIs have immediate feedback + if (this.isSinglecast()) { + const valueDB = this.endpoint.getNodeUnsafe()?.valueDB; + valueDB?.setValue( + getCurrentValueValueId(this.endpoint.index), + value, + ); + + // and verify the current value after a delay + this.schedulePoll({ property }); + } }; + private refreshTimeout: NodeJS.Timeout | undefined; + protected [POLL_VALUE]: PollValueImplementation = async ({ property, }): Promise => { @@ -121,8 +136,6 @@ export class BasicCCAPI extends CCAPI { } } - private refreshTimeout: NodeJS.Timeout | undefined; - public async set(targetValue: number): Promise { this.assertSupportsCommand(BasicCommand, BasicCommand.Set); @@ -131,30 +144,7 @@ export class BasicCCAPI extends CCAPI { endpoint: this.endpoint.index, targetValue, }); - if (this.isSinglecast()) { - // remember the value in case the device does not respond with a target value - this.endpoint - .getNodeUnsafe() - ?.valueDB.setValue( - getTargetValueValueId(this.endpoint.index), - targetValue, - { noEvent: true }, - ); - } await this.driver.sendCommand(cc, this.commandOptions); - - if (this.isSinglecast()) { - // Refresh the current value after a delay - if (this.refreshTimeout) clearTimeout(this.refreshTimeout); - setTimeout(async () => { - this.refreshTimeout = undefined; - try { - await this.get(); - } catch { - /* ignore */ - } - }, this.driver.options.timeouts.refreshValue).unref(); - } } } diff --git a/packages/zwave-js/src/lib/commandclass/BinarySwitchCC.ts b/packages/zwave-js/src/lib/commandclass/BinarySwitchCC.ts index 0b5f6f87578a..078b03343f1c 100644 --- a/packages/zwave-js/src/lib/commandclass/BinarySwitchCC.ts +++ b/packages/zwave-js/src/lib/commandclass/BinarySwitchCC.ts @@ -37,11 +37,11 @@ import { implementedVersion, } from "./CommandClass"; -function getTargetValueValueId(endpoint?: number): ValueID { +function getCurrentValueValueId(endpoint?: number): ValueID { return { commandClass: CommandClasses["Binary Switch"], endpoint, - property: "targetValue", + property: "currentValue", }; } @@ -108,30 +108,7 @@ export class BinarySwitchCCAPI extends CCAPI { targetValue, duration, }); - if (this.isSinglecast()) { - // remember the value in case the device does not respond with a target value - this.endpoint - .getNodeUnsafe() - ?.valueDB.setValue( - getTargetValueValueId(this.endpoint.index), - targetValue, - { noEvent: true }, - ); - } await this.driver.sendCommand(cc, this.commandOptions); - - if (this.isSinglecast()) { - // Refresh the current value after a delay - if (this.refreshTimeout) clearTimeout(this.refreshTimeout); - setTimeout(async () => { - this.refreshTimeout = undefined; - try { - await this.get(); - } catch { - /* ignore */ - } - }, duration?.toMilliseconds() ?? 1000).unref(); - } } protected [SET_VALUE]: SetValueImplementation = async ( @@ -145,6 +122,21 @@ export class BinarySwitchCCAPI extends CCAPI { throwWrongValueType(this.ccId, property, "boolean", typeof value); } await this.set(value); + + // If the command did not fail, assume that it succeeded and update the currentValue accordingly + // so UIs have immediate feedback + if (this.isSinglecast()) { + const valueDB = this.endpoint.getNodeUnsafe()?.valueDB; + valueDB?.setValue( + getCurrentValueValueId(this.endpoint.index), + value, + ); + + // Verify the current value after a delay + // TODO: #1321 + const duration = undefined as Duration | undefined; + this.schedulePoll({ property }, duration?.toMilliseconds() ?? 1000); + } }; protected [POLL_VALUE]: PollValueImplementation = async ({ diff --git a/packages/zwave-js/src/lib/commandclass/CentralSceneCC.ts b/packages/zwave-js/src/lib/commandclass/CentralSceneCC.ts index a4bbace0b8d6..9ea2fa4e7436 100644 --- a/packages/zwave-js/src/lib/commandclass/CentralSceneCC.ts +++ b/packages/zwave-js/src/lib/commandclass/CentralSceneCC.ts @@ -146,11 +146,6 @@ export class CentralSceneCCAPI extends CCAPI { slowRefresh, }); await this.driver.sendCommand(cc, this.commandOptions); - - if (this.isSinglecast()) { - // Refresh the current value - await this.getConfiguration(); - } } protected [SET_VALUE]: SetValueImplementation = async ( diff --git a/packages/zwave-js/src/lib/commandclass/ClimateControlScheduleCC.ts b/packages/zwave-js/src/lib/commandclass/ClimateControlScheduleCC.ts index 99ad7a72a49b..6ea88741c1ef 100644 --- a/packages/zwave-js/src/lib/commandclass/ClimateControlScheduleCC.ts +++ b/packages/zwave-js/src/lib/commandclass/ClimateControlScheduleCC.ts @@ -85,11 +85,6 @@ export class ClimateControlScheduleCCAPI extends CCAPI { switchPoints, }); await this.driver.sendCommand(cc, this.commandOptions); - - if (this.isSinglecast()) { - // Refresh the current value - await this.get(weekday); - } } public async get( @@ -168,11 +163,6 @@ export class ClimateControlScheduleCCAPI extends CCAPI { overrideState: state, }); await this.driver.sendCommand(cc, this.commandOptions); - - if (this.isSinglecast()) { - // Refresh the current value - await this.getOverride(); - } } } diff --git a/packages/zwave-js/src/lib/commandclass/ClockCC.ts b/packages/zwave-js/src/lib/commandclass/ClockCC.ts index d2e1263c7355..fcf1df2224f8 100644 --- a/packages/zwave-js/src/lib/commandclass/ClockCC.ts +++ b/packages/zwave-js/src/lib/commandclass/ClockCC.ts @@ -89,11 +89,6 @@ export class ClockCCAPI extends CCAPI { weekday: weekday ?? Weekday.Unknown, }); await this.driver.sendCommand(cc, this.commandOptions); - - if (this.isSinglecast()) { - // Refresh the current value - await this.get(); - } } } diff --git a/packages/zwave-js/src/lib/commandclass/ColorSwitchCC.ts b/packages/zwave-js/src/lib/commandclass/ColorSwitchCC.ts index d48b786b31fa..aa34b22165cc 100644 --- a/packages/zwave-js/src/lib/commandclass/ColorSwitchCC.ts +++ b/packages/zwave-js/src/lib/commandclass/ColorSwitchCC.ts @@ -224,7 +224,9 @@ export class ColorSwitchCCAPI extends CCAPI { await this.driver.sendCommand(cc, this.commandOptions); - // Assume it worked and update currentColor + // If the command did not fail, assume that it succeeded and update the values accordingly + // TODO: The API methods should not modify the value DB directly, but to do so + // this requires a nicer way of synchronizing hexColor with the others if (this.isSinglecast()) { const valueDB = this.endpoint.getNodeUnsafe()?.valueDB; if (valueDB) { @@ -330,8 +332,13 @@ export class ColorSwitchCCAPI extends CCAPI { await this.set({ [propertyKey]: value }); if (this.isSinglecast()) { - // Refresh the current value - await this.get(propertyKey); + // Verify the current value after a delay + // TODO: #1321 + const duration = undefined as Duration | undefined; + this.schedulePoll( + { property, propertyKey }, + duration?.toMilliseconds(), + ); } } else if (property === "hexColor") { // No property key, this is the hex color #rrggbb diff --git a/packages/zwave-js/src/lib/commandclass/ConfigurationCC.ts b/packages/zwave-js/src/lib/commandclass/ConfigurationCC.ts index 68e259665c87..21e6504a766f 100644 --- a/packages/zwave-js/src/lib/commandclass/ConfigurationCC.ts +++ b/packages/zwave-js/src/lib/commandclass/ConfigurationCC.ts @@ -221,10 +221,8 @@ export class ConfigurationCCAPI extends PhysicalCCAPI { await this.set(property, targetValue, valueSize as any); - // Refresh the current value and ignore potential timeouts - void this.get(property).catch(() => { - /* ignore */ - }); + // Verify the current value after a delay + this.schedulePoll({ property, propertyKey }, 1000); }; protected [POLL_VALUE]: PollValueImplementation = async ({ diff --git a/packages/zwave-js/src/lib/commandclass/DoorLockCC.ts b/packages/zwave-js/src/lib/commandclass/DoorLockCC.ts index 30267457d130..53f6ff329b32 100644 --- a/packages/zwave-js/src/lib/commandclass/DoorLockCC.ts +++ b/packages/zwave-js/src/lib/commandclass/DoorLockCC.ts @@ -130,6 +130,9 @@ export class DoorLockCCAPI extends PhysicalCCAPI { ); } await this.set(value); + + // Verify the current value after a delay + this.schedulePoll({ property }); } else if ( typeof property === "string" && configurationSetParameters.includes(property as any) @@ -164,6 +167,10 @@ export class DoorLockCCAPI extends PhysicalCCAPI { } await this.setConfiguration(config); + + // Refresh the current value + // TODO: #1321, #1521 + await this.getConfiguration(); } else { throwUnsupportedProperty(this.ccId, property); } @@ -275,17 +282,6 @@ export class DoorLockCCAPI extends PhysicalCCAPI { mode, }); await this.driver.sendCommand(cc, this.commandOptions); - - // Refresh the current value after a delay - if (this.refreshTimeout) clearTimeout(this.refreshTimeout); - setTimeout(async () => { - this.refreshTimeout = undefined; - try { - await this.get(); - } catch { - /* ignore */ - } - }, this.driver.options.timeouts.refreshValue).unref(); } public async setConfiguration( @@ -302,9 +298,6 @@ export class DoorLockCCAPI extends PhysicalCCAPI { ...configuration, }); await this.driver.sendCommand(cc, this.commandOptions); - - // Refresh the current value - await this.getConfiguration(); } // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types diff --git a/packages/zwave-js/src/lib/commandclass/IndicatorCC.ts b/packages/zwave-js/src/lib/commandclass/IndicatorCC.ts index 2e6f39827bd4..a8135b09d527 100644 --- a/packages/zwave-js/src/lib/commandclass/IndicatorCC.ts +++ b/packages/zwave-js/src/lib/commandclass/IndicatorCC.ts @@ -275,15 +275,6 @@ export class IndicatorCCAPI extends CCAPI { ...(typeof value === "number" ? { value } : { values: value }), }); await this.driver.sendCommand(cc, this.commandOptions); - - if (this.isSinglecast()) { - // Refresh the current value - if (typeof value === "number") { - await this.get(); - } else { - await this.get(value[0]?.indicatorId); - } - } } public async getSupported( diff --git a/packages/zwave-js/src/lib/commandclass/LanguageCC.ts b/packages/zwave-js/src/lib/commandclass/LanguageCC.ts index 8ea6de626471..be406ca381ed 100644 --- a/packages/zwave-js/src/lib/commandclass/LanguageCC.ts +++ b/packages/zwave-js/src/lib/commandclass/LanguageCC.ts @@ -71,11 +71,6 @@ export class LanguageCCAPI extends CCAPI { country, }); await this.driver.sendCommand(cc, this.commandOptions); - - if (this.isSinglecast()) { - // Refresh the current value - await this.get(); - } } } diff --git a/packages/zwave-js/src/lib/commandclass/LockCC.ts b/packages/zwave-js/src/lib/commandclass/LockCC.ts index d04b6be56179..e1d57892b176 100644 --- a/packages/zwave-js/src/lib/commandclass/LockCC.ts +++ b/packages/zwave-js/src/lib/commandclass/LockCC.ts @@ -76,9 +76,6 @@ export class LockCCAPI extends PhysicalCCAPI { locked, }); await this.driver.sendCommand(cc, this.commandOptions); - - // Refresh the current value - await this.get(); } protected [SET_VALUE]: SetValueImplementation = async ( @@ -92,6 +89,9 @@ export class LockCCAPI extends PhysicalCCAPI { throwWrongValueType(this.ccId, property, "boolean", typeof value); } await this.set(value); + + // Verify the current value after a delay + this.schedulePoll({ property }); }; protected [POLL_VALUE]: PollValueImplementation = async ({ diff --git a/packages/zwave-js/src/lib/commandclass/ManufacturerProprietaryCC.ts b/packages/zwave-js/src/lib/commandclass/ManufacturerProprietaryCC.ts index 7565f4f7ea71..dbbbd8641d20 100644 --- a/packages/zwave-js/src/lib/commandclass/ManufacturerProprietaryCC.ts +++ b/packages/zwave-js/src/lib/commandclass/ManufacturerProprietaryCC.ts @@ -122,8 +122,9 @@ export class ManufacturerProprietaryCCAPI extends CCAPI { // unsupported property key, ignore... return; } - // Refresh the current value - await this.fibaroVenetianBlindsGet(); + + // Verify the current value after a delay + this.schedulePoll({ property }); }; protected [POLL_VALUE]: PollValueImplementation = async ({ diff --git a/packages/zwave-js/src/lib/commandclass/MultilevelSwitchCC.ts b/packages/zwave-js/src/lib/commandclass/MultilevelSwitchCC.ts index 481565dfd098..f114eea767cf 100644 --- a/packages/zwave-js/src/lib/commandclass/MultilevelSwitchCC.ts +++ b/packages/zwave-js/src/lib/commandclass/MultilevelSwitchCC.ts @@ -92,7 +92,7 @@ export type MultilevelSwitchLevelChangeMetadata = ValueMetadata & { }; }; -function getCurrentValueValueID(endpoint: number): ValueID { +function getCurrentValueValueId(endpoint: number): ValueID { return { commandClass: CommandClasses["Multilevel Switch"], endpoint, @@ -100,14 +100,6 @@ function getCurrentValueValueID(endpoint: number): ValueID { }; } -function getTargetValueValueId(endpoint?: number): ValueID { - return { - commandClass: CommandClasses["Multilevel Switch"], - endpoint, - property: "targetValue", - }; -} - /** Returns the ValueID used to remember whether a node supports supervision on the start/stop level change commands*/ function getSuperviseStartStopLevelChangeValueId(): ValueID { return { @@ -158,9 +150,12 @@ export class MultilevelSwitchCCAPI extends CCAPI { * Sets the switch to a new value * @param targetValue The new target value for the switch * @param duration The optional duration to reach the target value. Available in V2+ + * @returns A promise indicating whether the command was completed */ - // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types - public async set(targetValue: number, duration?: Duration) { + public async set( + targetValue: number, + duration?: Duration, + ): Promise { this.assertSupportsCommand( MultilevelSwitchCommand, MultilevelSwitchCommand.Set, @@ -172,16 +167,6 @@ export class MultilevelSwitchCCAPI extends CCAPI { targetValue, duration, }); - if (this.isSinglecast()) { - // remember the value in case the device does not respond with a target value - this.endpoint - .getNodeUnsafe() - ?.valueDB.setValue( - getTargetValueValueId(this.endpoint.index), - targetValue, - { noEvent: true }, - ); - } // Multilevel Switch commands may take some time to be executed. // Therefore we try to supervise the command execution @@ -202,24 +187,10 @@ export class MultilevelSwitchCCAPI extends CCAPI { }, ); - // Refresh the current value - if ( + return ( !supervisionResult || supervisionResult.status === SupervisionStatus.Success - ) { - if (this.isSinglecast()) { - // Refresh the current value after a delay - if (this.refreshTimeout) clearTimeout(this.refreshTimeout); - setTimeout(async () => { - this.refreshTimeout = undefined; - try { - await this.get(); - } catch { - /* ignore */ - } - }, duration?.toMilliseconds() ?? this.driver.options.timeouts.refreshValue).unref(); - } - } + ); } public async startLevelChange( @@ -337,7 +308,28 @@ export class MultilevelSwitchCCAPI extends CCAPI { typeof value, ); } - await this.set(value); + const completed = await this.set(value); + + // If the command did not fail, assume that it succeeded and update the currentValue accordingly + // so UIs have immediate feedback + if (this.isSinglecast() && completed) { + const valueDB = this.endpoint.getNodeUnsafe()?.valueDB; + valueDB?.setValue( + getCurrentValueValueId(this.endpoint.index), + value, + ); + + // Verify the current value after a delay if the node does not support Supervision + if ( + !this.endpoint + .getNodeUnsafe() + ?.supportsCC(CommandClasses.Supervision) + ) { + // TODO: #1321 + const duration = undefined as Duration | undefined; + this.schedulePoll({ property }, duration?.toMilliseconds()); + } + } } else if (switchTypeProperties.includes(property as string)) { // Since the switch only supports one of the switch types, we would // need to check if the correct one is used. But since the names are @@ -363,7 +355,7 @@ export class MultilevelSwitchCCAPI extends CCAPI { const startLevel = this.endpoint .getNodeUnsafe() ?.getValue( - getCurrentValueValueID(this.endpoint.index), + getCurrentValueValueId(this.endpoint.index), ); // And perform the level change await this.startLevelChange({ diff --git a/packages/zwave-js/src/lib/commandclass/NodeNamingCC.ts b/packages/zwave-js/src/lib/commandclass/NodeNamingCC.ts index ebedc047d40b..6e774a648f49 100644 --- a/packages/zwave-js/src/lib/commandclass/NodeNamingCC.ts +++ b/packages/zwave-js/src/lib/commandclass/NodeNamingCC.ts @@ -77,13 +77,10 @@ export class NodeNamingAndLocationCCAPI extends PhysicalCCAPI { switch (property) { case "name": await this.setName(value); - // Refresh the current value - await this.getName(); break; case "location": await this.setLocation(value); - // Refresh the current value - await this.getLocation(); + break; } }; diff --git a/packages/zwave-js/src/lib/commandclass/ProtectionCC.ts b/packages/zwave-js/src/lib/commandclass/ProtectionCC.ts index b9cad77160ea..896920a5017c 100644 --- a/packages/zwave-js/src/lib/commandclass/ProtectionCC.ts +++ b/packages/zwave-js/src/lib/commandclass/ProtectionCC.ts @@ -247,11 +247,6 @@ export class ProtectionCCAPI extends CCAPI { rf, }); await this.driver.sendCommand(cc, this.commandOptions); - - if (this.isSinglecast()) { - // Refresh the current value - await this.get(); - } } // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types @@ -308,11 +303,6 @@ export class ProtectionCCAPI extends CCAPI { exclusiveControlNodeId: nodeId, }); await this.driver.sendCommand(cc, this.commandOptions); - - if (this.isSinglecast()) { - // Refresh the status - await this.getExclusiveControl(); - } } public async getTimeout(): Promise { @@ -344,11 +334,6 @@ export class ProtectionCCAPI extends CCAPI { timeout, }); await this.driver.sendCommand(cc, this.commandOptions); - - if (this.isSinglecast()) { - // Refresh the status - await this.getTimeout(); - } } } diff --git a/packages/zwave-js/src/lib/commandclass/SoundSwitchCC.ts b/packages/zwave-js/src/lib/commandclass/SoundSwitchCC.ts index 9984ae76dbca..436b70521262 100644 --- a/packages/zwave-js/src/lib/commandclass/SoundSwitchCC.ts +++ b/packages/zwave-js/src/lib/commandclass/SoundSwitchCC.ts @@ -141,11 +141,6 @@ export class SoundSwitchCCAPI extends CCAPI { defaultVolume, }); await this.driver.sendCommand(cc, this.commandOptions); - - if (this.isSinglecast()) { - // Refresh the current value - await this.getConfiguration(); - } } // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types @@ -188,11 +183,6 @@ export class SoundSwitchCCAPI extends CCAPI { volume, }); await this.driver.sendCommand(cc, this.commandOptions); - - if (this.isSinglecast()) { - // Refresh the current value - await this.getPlaying(); - } } public async stopPlaying(): Promise { @@ -208,11 +198,6 @@ export class SoundSwitchCCAPI extends CCAPI { volume: 0x00, }); await this.driver.sendCommand(cc, this.commandOptions); - - if (this.isSinglecast()) { - // Refresh the current value - await this.getPlaying(); - } } // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types @@ -277,6 +262,10 @@ export class SoundSwitchCCAPI extends CCAPI { } else { await this.stopPlaying(); } + if (this.isSinglecast()) { + // Verify the current value after a delay + this.schedulePoll({ property }); + } } else { throwUnsupportedProperty(this.ccId, property); } diff --git a/packages/zwave-js/src/lib/commandclass/ThermostatModeCC.ts b/packages/zwave-js/src/lib/commandclass/ThermostatModeCC.ts index d3c658d223de..2757b8098dbd 100644 --- a/packages/zwave-js/src/lib/commandclass/ThermostatModeCC.ts +++ b/packages/zwave-js/src/lib/commandclass/ThermostatModeCC.ts @@ -94,6 +94,11 @@ export class ThermostatModeCCAPI extends CCAPI { throwWrongValueType(this.ccId, property, "number", typeof value); } await this.set(value); + + if (this.isSinglecast()) { + // Verify the current value after a delay + this.schedulePoll({ property }); + } }; protected [POLL_VALUE]: PollValueImplementation = async ({ @@ -155,11 +160,6 @@ export class ThermostatModeCCAPI extends CCAPI { manufacturerData: manufacturerData as any, }); await this.driver.sendCommand(cc, this.commandOptions); - - if (this.isSinglecast()) { - // Refresh the current value - await this.get(); - } } public async getSupportedModes(): Promise< diff --git a/packages/zwave-js/src/lib/commandclass/ThermostatSetbackCC.ts b/packages/zwave-js/src/lib/commandclass/ThermostatSetbackCC.ts index 361186fbdc72..bfc7e9e00d26 100644 --- a/packages/zwave-js/src/lib/commandclass/ThermostatSetbackCC.ts +++ b/packages/zwave-js/src/lib/commandclass/ThermostatSetbackCC.ts @@ -113,11 +113,6 @@ export class ThermostatSetbackCCAPI extends CCAPI { setbackState, }); await this.driver.sendCommand(cc, this.commandOptions); - - if (this.isSinglecast()) { - // Refresh the current value - await this.get(); - } } } diff --git a/packages/zwave-js/src/lib/commandclass/ThermostatSetpointCC.ts b/packages/zwave-js/src/lib/commandclass/ThermostatSetpointCC.ts index a117f37e77f5..8882b90cb867 100644 --- a/packages/zwave-js/src/lib/commandclass/ThermostatSetpointCC.ts +++ b/packages/zwave-js/src/lib/commandclass/ThermostatSetpointCC.ts @@ -178,6 +178,11 @@ export class ThermostatSetpointCCAPI extends CCAPI { getSetpointScaleValueID(this.endpoint.index, propertyKey), ); await this.set(propertyKey, value, preferredScale ?? 0); + + if (this.isSinglecast()) { + // Verify the current value after a delay + this.schedulePoll({ property, propertyKey }); + } }; protected [POLL_VALUE]: PollValueImplementation = async ({ @@ -246,11 +251,6 @@ export class ThermostatSetpointCCAPI extends CCAPI { scale, }); await this.driver.sendCommand(cc, this.commandOptions); - - if (this.isSinglecast()) { - // Refresh the current value - await this.get(setpointType); - } } // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types diff --git a/packages/zwave-js/src/lib/commandclass/TimeCC.ts b/packages/zwave-js/src/lib/commandclass/TimeCC.ts index c95bc216f188..6643107969a5 100644 --- a/packages/zwave-js/src/lib/commandclass/TimeCC.ts +++ b/packages/zwave-js/src/lib/commandclass/TimeCC.ts @@ -102,11 +102,6 @@ export class TimeCCAPI extends CCAPI { dstEnd: timezone.endDate, }); await this.driver.sendCommand(cc, this.commandOptions); - - if (this.isSinglecast()) { - // Refresh the current value - await this.getTimezone(); - } } public async getTimezone(): Promise { diff --git a/packages/zwave-js/src/lib/commandclass/TimeParametersCC.ts b/packages/zwave-js/src/lib/commandclass/TimeParametersCC.ts index e107eb06787a..b9cab6bb5cb9 100644 --- a/packages/zwave-js/src/lib/commandclass/TimeParametersCC.ts +++ b/packages/zwave-js/src/lib/commandclass/TimeParametersCC.ts @@ -166,11 +166,6 @@ export class TimeParametersCCAPI extends CCAPI { dateAndTime, }); await this.driver.sendCommand(cc, this.commandOptions); - - if (this.isSinglecast()) { - // Refresh the current value - await this.get(); - } } } diff --git a/packages/zwave-js/src/lib/commandclass/UserCodeCC.ts b/packages/zwave-js/src/lib/commandclass/UserCodeCC.ts index b9f0efa44997..e1d6f81bebae 100644 --- a/packages/zwave-js/src/lib/commandclass/UserCodeCC.ts +++ b/packages/zwave-js/src/lib/commandclass/UserCodeCC.ts @@ -415,6 +415,9 @@ export class UserCodeCCAPI extends PhysicalCCAPI { } else { throwUnsupportedProperty(this.ccId, property); } + + // Verify the current value after a delay + this.schedulePoll({ property, propertyKey }); }; protected [POLL_VALUE]: PollValueImplementation = async ({ @@ -539,9 +542,6 @@ export class UserCodeCCAPI extends PhysicalCCAPI { }); await this.driver.sendCommand(cc, this.commandOptions); - - // Refresh the current value - await this.get(userId); } /** Configures multiple user codes */ @@ -579,9 +579,6 @@ export class UserCodeCCAPI extends PhysicalCCAPI { }); await this.driver.sendCommand(cc, this.commandOptions); } - - // Refresh the current value - await this.get(userId); } // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types @@ -643,9 +640,6 @@ export class UserCodeCCAPI extends PhysicalCCAPI { }); await this.driver.sendCommand(cc, this.commandOptions); - - // Refresh the current value - await this.getKeypadMode(); } public async getMasterCode(): Promise { @@ -678,9 +672,6 @@ export class UserCodeCCAPI extends PhysicalCCAPI { }); await this.driver.sendCommand(cc, this.commandOptions); - - // Refresh the current value - await this.getMasterCode(); } public async getUserCodeChecksum(): Promise { diff --git a/packages/zwave-js/src/lib/commandclass/WakeUpCC.ts b/packages/zwave-js/src/lib/commandclass/WakeUpCC.ts index 0516b186dbdd..b07b15c0943b 100644 --- a/packages/zwave-js/src/lib/commandclass/WakeUpCC.ts +++ b/packages/zwave-js/src/lib/commandclass/WakeUpCC.ts @@ -77,6 +77,11 @@ export class WakeUpCCAPI extends CCAPI { throwWrongValueType(this.ccId, property, "number", typeof value); } await this.setInterval(value, this.driver.controller.ownNodeId ?? 1); + + if (this.isSinglecast()) { + // Verify the current value after a delay + this.schedulePoll({ property }); + } }; protected [POLL_VALUE]: PollValueImplementation = async ({ @@ -145,11 +150,6 @@ export class WakeUpCCAPI extends CCAPI { controllerNodeId, }); await this.driver.sendCommand(cc, this.commandOptions); - - if (this.isSinglecast()) { - // Refresh the current value - await this.getInterval(); - } } public async sendNoMoreInformation(): Promise { diff --git a/packages/zwave-js/src/lib/node/Node.ts b/packages/zwave-js/src/lib/node/Node.ts index c999e4b66e43..833d5a088913 100644 --- a/packages/zwave-js/src/lib/node/Node.ts +++ b/packages/zwave-js/src/lib/node/Node.ts @@ -671,6 +671,9 @@ export class ZWaveNode extends Endpoint { }, value, ); + // If the call did not throw, assume that the call was successful and remember the new value + this._valueDB.setValue(valueId, value, { noEvent: true }); + return true; } catch (e: unknown) { // Define which errors during setValue are expected and won't crash