UNPKG

zwave-js

Version:

Z-Wave driver written entirely in JavaScript/TypeScript

155 lines 6.87 kB
import { CommandClass, defaultCCValueOptions, getCCValues, } from "@zwave-js/cc"; import { ValueDB, ValueMetadata, applicationCCs, getCCName, } from "@zwave-js/core"; import { pick } from "@zwave-js/shared"; import * as nodeUtils from "../utils.js"; import { NodeWakeupMixin } from "./30_Wakeup.js"; export class NodeValuesMixin extends NodeWakeupMixin { constructor(nodeId, driver, endpointIndex, deviceClass, supportedCCs, valueDB) { // Define this node's intrinsic endpoint as the root device (0) super(nodeId, driver, endpointIndex, deviceClass, supportedCCs); this._valueDB = valueDB ?? new ValueDB(nodeId, driver.valueDB, driver.metadataDB); // Pass value events to our listeners for (const event of [ "value added", "value updated", "value removed", "value notification", "metadata updated", ]) { this._valueDB.on(event, this.translateValueEvent.bind(this, event)); } } _valueDB; get valueDB() { return this._valueDB; } getValue(valueId) { return this._valueDB.getValue(valueId); } getValueTimestamp(valueId) { return this._valueDB.getTimestamp(valueId); } getValueMetadata(valueId) { // Check if a corresponding CC value is defined for this value ID // so we can extend the returned metadata const definedCCValues = getCCValues(valueId.commandClass); let valueOptions; let meta; if (definedCCValues) { const value = Object.values(definedCCValues) .find((v) => v?.is(valueId)); if (value) { if (typeof value !== "function") { meta = value.meta; } valueOptions = value.options; } } const existingMetadata = this._valueDB.getMetadata(valueId); return { // The priority for returned metadata is valueDB > defined value > Any (default) ...(existingMetadata ?? meta ?? ValueMetadata.Any), // ...except for these flags, which are taken from defined values: stateful: valueOptions?.stateful ?? defaultCCValueOptions.stateful, secret: valueOptions?.secret ?? defaultCCValueOptions.secret, }; } /** * Enhances the raw event args of the ValueDB so it can be consumed better by applications */ translateValueEvent(eventName, arg) { // Try to retrieve the speaking CC name const outArg = nodeUtils.translateValueID(this.driver, this, arg); // This can happen for value updated events if ("source" in outArg) delete outArg.source; const loglevel = this.driver.getLogConfig().level; // If this is a metadata event, make sure we return the merged metadata if ("metadata" in outArg) { outArg.metadata = this .getValueMetadata(arg); } const ccInstance = CommandClass.createInstanceUnchecked(this, arg.commandClass); const isInternalValue = !!ccInstance?.isInternalValue(arg); // Check whether this value change may be logged const isSecretValue = !!ccInstance?.isSecretValue(arg); if (loglevel === "silly") { this.driver.controllerLog.logNode(this.id, { message: `[translateValueEvent: ${eventName}] commandClass: ${getCCName(arg.commandClass)} endpoint: ${arg.endpoint} property: ${arg.property} propertyKey: ${arg.propertyKey} internal: ${isInternalValue} secret: ${isSecretValue} event source: ${arg.source}`, level: "silly", }); } if (!isSecretValue && arg.source !== "driver") { // Log the value change, except for updates caused by the driver itself // I don't like the splitting and any but its the easiest solution here const [changeTarget, changeType] = eventName.split(" "); const logArgument = { ...outArg, nodeId: this.id, internal: isInternalValue, }; if (changeTarget === "value") { this.driver.controllerLog.value(changeType, logArgument); } else if (changeTarget === "metadata") { this.driver.controllerLog.metadataUpdated(logArgument); } } // Don't expose value events for internal value IDs... if (isInternalValue) return; if (loglevel === "silly") { this.driver.controllerLog.logNode(this.id, { message: `[translateValueEvent: ${eventName}] is root endpoint: ${!arg.endpoint} is application CC: ${applicationCCs.includes(arg.commandClass)} should hide root values: ${nodeUtils.shouldHideRootApplicationCCValues(this.driver, this.id)}`, level: "silly", }); } // ... and root values ID that mirrors endpoint functionality if ( // Only root endpoint values need to be filtered !arg.endpoint // Only application CCs need to be filtered && applicationCCs.includes(arg.commandClass) // and only if the endpoints are not unnecessary and the root values mirror them && nodeUtils.shouldHideRootApplicationCCValues(this.driver, this.id)) { // Iterate through all possible non-root endpoints of this node and // check if there is a value ID that mirrors root endpoint functionality for (const endpoint of nodeUtils.getEndpointIndizes(this.driver, this.id)) { const possiblyMirroredValueID = { // same CC, property and key ...pick(arg, ["commandClass", "property", "propertyKey"]), // but different endpoint endpoint, }; if (this.valueDB.hasValue(possiblyMirroredValueID)) { if (loglevel === "silly") { this.driver.controllerLog.logNode(this.id, { message: `[translateValueEvent: ${eventName}] found mirrored value ID on different endpoint, ignoring event: commandClass: ${getCCName(possiblyMirroredValueID.commandClass)} endpoint: ${possiblyMirroredValueID.endpoint} property: ${possiblyMirroredValueID.property} propertyKey: ${possiblyMirroredValueID.propertyKey}`, level: "silly", }); } return; } } } // And pass the translated event to our listeners this._emit(eventName, this, outArg); } } //# sourceMappingURL=40_Values.js.map