UNPKG

zwave-js

Version:

Z-Wave driver written entirely in JavaScript/TypeScript

220 lines 9.05 kB
import { WindowCoveringCCGet, WindowCoveringCCReport, WindowCoveringCCSet, WindowCoveringCCStartLevelChange, WindowCoveringCCStopLevelChange, WindowCoveringCCSupportedGet, WindowCoveringCCSupportedReport, } from "@zwave-js/cc/WindowCoveringCC"; import { CommandClasses, Duration } from "@zwave-js/core"; import { setTimer } from "@zwave-js/shared"; import { getTransitionCurrentValue, getTransitionRemainingDuration, startTransition, stopTransition, } from "./MockTransition.js"; const defaultCapabilities = { supportedParameters: [], travelTime: 5000, }; const STATE_KEY_PREFIX = "WindowCovering_"; function currentValueKey(param) { return `${STATE_KEY_PREFIX}currentValue_${param}`; } function transitionKey(param) { return `${STATE_KEY_PREFIX}transition_${param}`; } /** Stops any running transition for the given parameter and snaps state. */ function stopParameterTransition(self, param) { const existing = self.state.get(transitionKey(param)); if (existing) { const value = stopTransition(existing); self.state.set(currentValueKey(param), value); self.state.delete(transitionKey(param)); return { wasSupervised: existing.supervised }; } } /** * Stops any existing transition for a parameter, then starts a new one. * Manages all state reads/writes. */ function beginTransition(controller, self, receivedCC, param, targetValue, travelTime, supervised, onCompleteSupervised, onAbortSupervised) { stopParameterTransition(self, param); const currentValue = self.state.get(currentValueKey(param)) ?? 0; const transition = startTransition({ currentValue, targetValue, duration: travelTime, supervised, onComplete: async () => { self.state.set(currentValueKey(param), targetValue); self.state.delete(transitionKey(param)); if (supervised) { await onCompleteSupervised?.(); return; } const cc = new WindowCoveringCCReport({ nodeId: controller.ownNodeId, parameter: param, currentValue: targetValue, targetValue, duration: new Duration(0, "seconds"), }); await self.sendResponse(receivedCC, { action: "sendCC", cc, }); }, onAbort: async () => { if (!supervised) return; await onAbortSupervised?.(); }, }); if (transition) { self.state.set(transitionKey(param), transition); return transition.durationMs; } else { self.state.set(currentValueKey(param), targetValue); return 0; } } const respondToWindowCoveringSupportedGet = { handleCC(controller, self, receivedCC) { if (receivedCC instanceof WindowCoveringCCSupportedGet) { const capabilities = { ...defaultCapabilities, ...self.getCCCapabilities(CommandClasses["Window Covering"], receivedCC.endpointIndex), }; const cc = new WindowCoveringCCSupportedReport({ nodeId: controller.ownNodeId, supportedParameters: capabilities.supportedParameters, }); return { action: "sendCC", cc }; } }, }; const respondToWindowCoveringGet = { handleCC(controller, self, receivedCC) { if (receivedCC instanceof WindowCoveringCCGet) { const param = receivedCC.parameter; const transition = self.state.get(transitionKey(param)); let currentValue; let targetValue; let duration; if (transition) { currentValue = getTransitionCurrentValue(transition); targetValue = transition.targetValue; duration = getTransitionRemainingDuration(transition); } else { currentValue = self.state.get(currentValueKey(param)) ?? 0; targetValue = currentValue; duration = new Duration(0, "seconds"); } const cc = new WindowCoveringCCReport({ nodeId: controller.ownNodeId, parameter: param, currentValue, targetValue, duration, }); return { action: "sendCC", cc }; } }, }; const respondToWindowCoveringSet = { handleCC(controller, self, receivedCC) { if (receivedCC instanceof WindowCoveringCCSet) { const capabilities = { ...defaultCapabilities, ...self.getCCCapabilities(CommandClasses["Window Covering"], receivedCC.endpointIndex), }; const supervised = receivedCC.isEncapsulatedWith(CommandClasses.Supervision); const supervisedState = supervised ? { activeParams: new Set(), finished: false, } : undefined; let durationMs = 0; for (const { parameter, value } of receivedCC.targetValues) { const completeSupervised = async () => { if (!supervisedState || supervisedState.finished) return; supervisedState.finished = true; await self.sendResponse(receivedCC, { action: "ok", }); }; const abortSupervised = async () => { if (!supervisedState || supervisedState.finished) return; supervisedState.finished = true; supervisedState.activeParams.delete(parameter); for (const otherParam of supervisedState.activeParams) { stopParameterTransition(self, otherParam); } supervisedState.activeParams.clear(); await self.sendResponse(receivedCC, { action: "fail", }); }; const transitionDurationMs = beginTransition(controller, self, receivedCC, parameter, value, capabilities.travelTime, supervised, completeSupervised, abortSupervised); durationMs = Math.max(durationMs, transitionDurationMs); if (transitionDurationMs > 0 && supervisedState) { supervisedState.activeParams.add(parameter); } } return { action: "ok", durationMs, }; } }, }; const respondToWindowCoveringStartLevelChange = { handleCC(controller, self, receivedCC) { if (receivedCC instanceof WindowCoveringCCStartLevelChange) { const capabilities = { ...defaultCapabilities, ...self.getCCCapabilities(CommandClasses["Window Covering"], receivedCC.endpointIndex), }; const targetValue = receivedCC.direction === "up" ? 99 : 0; const supervised = receivedCC.isEncapsulatedWith(CommandClasses.Supervision); const durationMs = beginTransition(controller, self, receivedCC, receivedCC.parameter, targetValue, capabilities.travelTime, supervised, async () => self.sendResponse(receivedCC, { action: "ok", }), async () => self.sendResponse(receivedCC, { action: "fail", })); return { action: "ok", durationMs, }; } }, }; const respondToWindowCoveringStopLevelChange = { handleCC(controller, self, receivedCC) { if (receivedCC instanceof WindowCoveringCCStopLevelChange) { const param = receivedCC.parameter; const stoppedTransition = stopParameterTransition(self, param); const currentValue = (self.state.get(currentValueKey(param)) ?? 0); if (!stoppedTransition?.wasSupervised) { // Send a delayed report with the final state setTimer(async () => { const cc = new WindowCoveringCCReport({ nodeId: controller.ownNodeId, parameter: param, currentValue, targetValue: currentValue, duration: new Duration(0, "seconds"), }); await self.sendResponse(receivedCC, { action: "sendCC", cc, }); }, 100).unref(); } return { action: "ok" }; } }, }; export const WindowCoveringCCBehaviors = [ respondToWindowCoveringSupportedGet, respondToWindowCoveringGet, respondToWindowCoveringSet, respondToWindowCoveringStartLevelChange, respondToWindowCoveringStopLevelChange, ]; //# sourceMappingURL=WindowCovering.js.map