UNPKG

zwave-js

Version:

Z-Wave driver written entirely in JavaScript/TypeScript

141 lines 5.49 kB
import { WakeUpCCValues, } from "@zwave-js/cc"; import { CommandClasses, ZWaveErrorCodes, isZWaveError, } from "@zwave-js/core"; import { getErrorMessage } from "@zwave-js/shared"; import { isObject } from "alcalzone-shared/typeguards"; export function getDefaultWakeUpHandlerStore() { return { lastWakeUp: undefined, }; } /** Handles the receipt of a Wake Up notification */ export function handleWakeUpNotification(ctx, node, _command, store) { ctx.logNode(node.id, { message: `received wakeup notification`, direction: "inbound", }); // It can happen that the node has not told us that it supports the Wake Up CC // https://sentry.io/share/issue/6a681729d7db46d591f1dcadabe8d02e/ // To avoid a crash, mark it as supported if (node.getCCVersion(CommandClasses["Wake Up"]) === 0) { node.addCC(CommandClasses["Wake Up"], { isSupported: true, version: 1, }); } node.markAsAwake(); // From the specs: // A controlling node SHOULD read the Wake Up Interval of a supporting node when the delays between // Wake Up periods are larger than what was last set at the supporting node. const now = Date.now(); if (store.lastWakeUp) { // we've already measured the wake up interval, so we can check whether a refresh is necessary const wakeUpInterval = node.getValue(WakeUpCCValues.wakeUpInterval.id) ?? 1; // The wakeup interval is specified in seconds. Also add 5 minutes tolerance to avoid // unnecessary queries since there might be some delay. A wakeup interval of 0 means manual wakeup, // so the interval shouldn't be verified if (wakeUpInterval > 0 && (now - store.lastWakeUp) / 1000 > wakeUpInterval + 5 * 60) { node.commandClasses["Wake Up"].getInterval().catch(() => { // Don't throw if there's an error }); } } store.lastWakeUp = now; // Some legacy devices expect us to query them on wake up in order to function correctly if (node.deviceConfig?.compat?.queryOnWakeup) { void compatDoWakeupQueries(ctx, node); } else if (!node.deviceConfig?.compat?.disableAutoRefresh) { // For other devices we may have to refresh their values from time to time void node.autoRefreshValues().catch(() => { // ignore }); } } async function compatDoWakeupQueries(ctx, node) { if (!node.deviceConfig?.compat?.queryOnWakeup) return; ctx.logNode(node.id, { message: `expects some queries after wake up, so it shall receive`, direction: "none", }); for (const [ccName, apiMethod, ...args] of node.deviceConfig.compat .queryOnWakeup) { ctx.logNode(node.id, { message: `compat query "${ccName}"::${apiMethod}(${args .map((arg) => JSON.stringify(arg)) .join(", ")})`, direction: "none", }); // Try to access the API - if it doesn't work, skip this option let API; try { API = node.commandClasses[ccName].withOptions({ // Tag the resulting transactions as compat queries tag: "compat", // Do not retry them or they may cause congestion if the node is asleep again maxSendAttempts: 1, // This is for a sleeping node - there's no point in keeping the transactions when the node is asleep expire: 10000, }); } catch { ctx.logNode(node.id, { message: `could not access API, skipping query`, direction: "none", level: "warn", }); continue; } if (!API.isSupported()) { ctx.logNode(node.id, { message: `API not supported, skipping query`, direction: "none", level: "warn", }); continue; } else if (!API[apiMethod]) { ctx.logNode(node.id, { message: `method ${apiMethod} not found on API, skipping query`, direction: "none", level: "warn", }); continue; } // Retrieve the method const method = API[apiMethod].bind(API); // And replace "smart" arguments with their corresponding value const methodArgs = args.map((arg) => { if (isObject(arg)) { const valueId = { commandClass: API.ccId, ...arg, }; return node.getValue(valueId); } return arg; }); // Do the API call and ignore/log any errors try { await method(...methodArgs); ctx.logNode(node.id, { message: `API call successful`, direction: "none", }); } catch (e) { ctx.logNode(node.id, { message: `error during API call: ${getErrorMessage(e)}`, direction: "none", level: "warn", }); if (isZWaveError(e) && e.code === ZWaveErrorCodes.Controller_MessageExpired) { // A compat query expired - no point in trying the others too return; } } } } //# sourceMappingURL=WakeUpCC.js.map