UNPKG

zwave-js

Version:

Z-Wave driver written entirely in JavaScript/TypeScript

833 lines 40.7 kB
// oxlint-disable typescript/no-misused-spread import { CommandClass, WakeUpTime } from "@zwave-js/cc"; import { ZWaveProtocolCC, ZWaveProtocolCCAssignSUCReturnRoute, ZWaveProtocolCCNodeInformationFrame, ZWaveProtocolCCRequestNodeInformationFrame, } from "@zwave-js/cc/ZWaveProtocolCC"; import { NodeType, TransmitOptions, TransmitStatus, ZWaveDataRate, ZWaveErrorCodes, isZWaveError, } from "@zwave-js/core"; import { AddNodeStatus, AddNodeToNetworkRequest, AddNodeToNetworkRequestStatusReport, AddNodeType, ApplicationCommandRequest, ApplicationUpdateRequestNodeInfoReceived, ApplicationUpdateRequestNodeInfoRequestFailed, AssignSUCReturnRouteRequest, AssignSUCReturnRouteRequestTransmitReport, AssignSUCReturnRouteResponse, DeleteSUCReturnRouteRequest, DeleteSUCReturnRouteRequestTransmitReport, DeleteSUCReturnRouteResponse, GetControllerCapabilitiesRequest, GetControllerCapabilitiesResponse, GetControllerIdRequest, GetControllerIdResponse, GetControllerVersionRequest, GetControllerVersionResponse, GetNodeProtocolInfoRequest, GetNodeProtocolInfoResponse, GetSUCNodeIdRequest, GetSUCNodeIdResponse, GetSerialApiCapabilitiesRequest, GetSerialApiCapabilitiesResponse, GetSerialApiInitDataRequest, GetSerialApiInitDataResponse, RemoveNodeFromNetworkRequest, RemoveNodeFromNetworkRequestStatusReport, RemoveNodeStatus, RemoveNodeType, RequestNodeInfoRequest, RequestNodeInfoResponse, SendDataBridgeRequest, SendDataBridgeRequestTransmitReport, SendDataBridgeResponse, SendDataMulticastBridgeRequest, SendDataMulticastBridgeRequestTransmitReport, SendDataMulticastBridgeResponse, SendDataMulticastRequest, SendDataMulticastRequestTransmitReport, SendDataMulticastResponse, SendDataRequest, SendDataRequestTransmitReport, SendDataResponse, SerialAPIStartedRequest, SerialAPIWakeUpReason, SoftResetRequest, } from "@zwave-js/serial/serialapi"; import { MOCK_FRAME_ACK_TIMEOUT, MockNode, MockZWaveFrameType, createMockZWaveRequestFrame, } from "@zwave-js/testing"; import { wait } from "alcalzone-shared/async"; import { createDefaultMockNodeBehaviors } from "../../Testing.js"; import { MockControllerCommunicationState, MockControllerInclusionState, MockControllerStateKeys, } from "./MockControllerState.js"; import { determineNIF } from "./NodeInformationFrame.js"; function createLazySendDataPayload(controller, node, msg) { return async () => { try { const cmd = await CommandClass.parse(msg.serializedCC, { sourceNodeId: controller.ownNodeId, __internalIsMockNode: true, ...node.encodingContext, ...node.securityManagers, // The frame type is always singlecast because the controller sends it to the node frameType: "singlecast", }); // Store the command because assertReceivedHostMessage needs it // @ts-expect-error msg.command = cmd; return cmd; } catch (e) { if (isZWaveError(e)) { if (e.code === ZWaveErrorCodes.CC_NotImplemented) { // The whole CC is not implemented yet. If this happens in tests, it is because we sent a raw CC. try { const cmd = new CommandClass({ nodeId: controller.ownNodeId, ccId: msg.payload[0], ccCommand: msg.payload[1], payload: msg.payload.subarray(2), }); // Store the command because assertReceivedHostMessage needs it // @ts-expect-error msg.command = cmd; return cmd; } catch (e) { console.error(e.message); throw e; } } else if (e.code === ZWaveErrorCodes.Deserialization_NotImplemented) { // We want to know when we're using a command in tests that cannot be decoded yet console.error(e.message); throw e; } } console.error(e); throw e; } }; } const respondToGetControllerId = { async onHostMessage(controller, msg) { if (msg instanceof GetControllerIdRequest) { const ret = new GetControllerIdResponse({ homeId: controller.homeId, ownNodeId: controller.ownNodeId, }); await controller.sendMessageToHost(ret); return true; } }, }; const respondToGetSerialApiCapabilities = { async onHostMessage(controller, msg) { if (msg instanceof GetSerialApiCapabilitiesRequest) { const ret = new GetSerialApiCapabilitiesResponse({ ...controller.capabilities, }); await controller.sendMessageToHost(ret); return true; } }, }; const respondToGetControllerVersion = { async onHostMessage(controller, msg) { if (msg instanceof GetControllerVersionRequest) { const ret = new GetControllerVersionResponse({ ...controller.capabilities, }); await controller.sendMessageToHost(ret); return true; } }, }; const respondToGetControllerCapabilities = { async onHostMessage(controller, msg) { if (msg instanceof GetControllerCapabilitiesRequest) { const ret = new GetControllerCapabilitiesResponse({ ...controller.capabilities, }); await controller.sendMessageToHost(ret); return true; } }, }; const respondToGetSUCNodeId = { async onHostMessage(controller, msg) { if (msg instanceof GetSUCNodeIdRequest) { const sucNodeId = controller.capabilities.isStaticUpdateController ? controller.ownNodeId : controller.capabilities.sucNodeId; const ret = new GetSUCNodeIdResponse({ sucNodeId, }); await controller.sendMessageToHost(ret); return true; } }, }; const respondToGetSerialApiInitData = { async onHostMessage(controller, msg) { if (msg instanceof GetSerialApiInitDataRequest) { const nodeIds = new Set(controller.nodes.keys()); nodeIds.add(controller.ownNodeId); const ret = new GetSerialApiInitDataResponse({ zwaveApiVersion: controller.capabilities.zwaveApiVersion, isPrimary: !controller.capabilities.isSecondary, nodeType: NodeType.Controller, supportsTimers: controller.capabilities.supportsTimers, isSIS: controller.capabilities.isSISPresent && controller.capabilities.isStaticUpdateController, nodeIds: [...nodeIds], zwaveChipType: controller.capabilities.zwaveChipType, }); await controller.sendMessageToHost(ret); return true; } }, }; const respondToSoftReset = { onHostMessage(controller, msg) { if (msg instanceof SoftResetRequest) { const ret = new SerialAPIStartedRequest({ wakeUpReason: SerialAPIWakeUpReason.SoftwareReset, watchdogEnabled: controller.capabilities.watchdogEnabled, isListening: true, ...determineNIF(), supportsLongRange: controller.capabilities.supportsLongRange, }); setImmediate(async () => { await controller.sendMessageToHost(ret); }); return true; } }, }; const respondToGetNodeProtocolInfo = { async onHostMessage(controller, msg) { if (msg instanceof GetNodeProtocolInfoRequest) { if (msg.requestedNodeId === controller.ownNodeId) { const ret = new GetNodeProtocolInfoResponse({ ...determineNIF(), nodeType: NodeType.Controller, isListening: true, isFrequentListening: false, isRouting: true, supportsSecurity: false, supportsBeaming: true, supportedDataRates: [9600, 40000, 100000], optionalFunctionality: true, protocolVersion: 3, }); await controller.sendMessageToHost(ret); return true; } else if (controller.nodes.has(msg.requestedNodeId)) { const nodeCaps = controller.nodes.get(msg.requestedNodeId).capabilities; const ret = new GetNodeProtocolInfoResponse({ ...nodeCaps, }); await controller.sendMessageToHost(ret); return true; } } }, }; const handleSendData = { async onHostMessage(controller, msg) { if (msg instanceof SendDataRequest) { // Check if this command is legal right now const state = controller.state.get(MockControllerStateKeys.CommunicationState); if (state != undefined && state !== MockControllerCommunicationState.Idle) { throw new Error("Received SendDataRequest while not idle"); } // Put the controller into sending state controller.state.set(MockControllerStateKeys.CommunicationState, MockControllerCommunicationState.Sending); // Notify the host that the message was sent const res = new SendDataResponse({ wasSent: true, }); await controller.sendMessageToHost(res); // We deferred parsing of the CC because it requires the node's host to do so. // Now we can do that. Also set the CC node ID to the controller's own node ID, // so CC knows it came from the controller's node ID. const node = controller.nodes.get(msg.getNodeId()); // Create a lazy frame, so it can be deserialized on the node after a short delay to simulate radio transmission const lazyPayload = createLazySendDataPayload(controller, node, msg); const ackRequested = !!(msg.transmitOptions & TransmitOptions.ACK); const lazyFrame = createMockZWaveRequestFrame(lazyPayload, { ackRequested, }); const ackPromise = controller.sendToNode(node, lazyFrame); if (msg.callbackId !== 0) { // Put the controller into waiting state controller.state.set(MockControllerStateKeys.CommunicationState, MockControllerCommunicationState.WaitingForNode); // If an ACK is requested, wait for the ACK and notify the host let ack = true; if (ackRequested) { try { const ackResult = await ackPromise; ack = !!ackResult?.ack; } catch (e) { // We want to know when we're using a command in tests that cannot be decoded yet if (isZWaveError(e) && e.code === ZWaveErrorCodes .Deserialization_NotImplemented) { console.error(e.message); throw e; } // Treat all other errors as no response ack = false; } } controller.state.set(MockControllerStateKeys.CommunicationState, MockControllerCommunicationState.Idle); const cb = new SendDataRequestTransmitReport({ callbackId: msg.callbackId, transmitStatus: ack ? TransmitStatus.OK : TransmitStatus.NoAck, }); await controller.sendMessageToHost(cb); } else { // No callback was requested, we're done controller.state.set(MockControllerStateKeys.CommunicationState, MockControllerCommunicationState.Idle); } return true; } }, }; const handleSendDataMulticast = { async onHostMessage(controller, msg) { if (msg instanceof SendDataMulticastRequest) { // Check if this command is legal right now const state = controller.state.get(MockControllerStateKeys.CommunicationState); if (state != undefined && state !== MockControllerCommunicationState.Idle) { throw new Error("Received SendDataMulticastRequest while not idle"); } // Put the controller into sending state controller.state.set(MockControllerStateKeys.CommunicationState, MockControllerCommunicationState.Sending); // Notify the host that the message was sent const res = new SendDataMulticastResponse({ wasSent: true, }); await controller.sendMessageToHost(res); // We deferred parsing of the CC because it requires the node's host to do so. // Now we can do that. Also set the CC node ID to the controller's own node ID, // so CC knows it came from the controller's node ID. const nodeIds = msg.nodeIds; const ackRequested = !!(msg.transmitOptions & TransmitOptions.ACK); const ackPromises = nodeIds.map((nodeId) => { const node = controller.nodes.get(nodeId); // Create a lazy frame, so it can be deserialized on the node after a short delay to simulate radio transmission const lazyPayload = createLazySendDataPayload(controller, node, msg); const lazyFrame = createMockZWaveRequestFrame(lazyPayload, { ackRequested, }); const ackPromise = controller.sendToNode(node, lazyFrame); return ackPromise; }); if (msg.callbackId !== 0) { // Put the controller into waiting state controller.state.set(MockControllerStateKeys.CommunicationState, MockControllerCommunicationState.WaitingForNode); // If an ACK is requested, wait for the ACKs and notify the host let ack = true; if (ackRequested) { try { const ackResults = await Promise.all(ackPromises); ack = ackResults.every((result) => !!result?.ack); } catch (e) { // We want to know when we're using a command in tests that cannot be decoded yet if (isZWaveError(e) && e.code === ZWaveErrorCodes .Deserialization_NotImplemented) { console.error(e.message); throw e; } // Treat all other errors as no response ack = false; } } controller.state.set(MockControllerStateKeys.CommunicationState, MockControllerCommunicationState.Idle); const cb = new SendDataMulticastRequestTransmitReport({ callbackId: msg.callbackId, transmitStatus: ack ? TransmitStatus.OK : TransmitStatus.NoAck, }); await controller.sendMessageToHost(cb); } else { // No callback was requested, we're done controller.state.set(MockControllerStateKeys.CommunicationState, MockControllerCommunicationState.Idle); } return true; } }, }; const handleSendDataBridge = { async onHostMessage(controller, msg) { if (msg instanceof SendDataBridgeRequest) { // Check if this command is legal right now const state = controller.state.get(MockControllerStateKeys.CommunicationState); if (state != undefined && state !== MockControllerCommunicationState.Idle) { throw new Error("Received SendDataBridgeRequest while not idle"); } // Put the controller into sending state controller.state.set(MockControllerStateKeys.CommunicationState, MockControllerCommunicationState.Sending); // Notify the host that the message was sent const res = new SendDataBridgeResponse({ wasSent: true, }); await controller.sendMessageToHost(res); // We deferred parsing of the CC because it requires the node's host to do so. // Now we can do that. Also set the CC node ID to the controller's own node ID, // so CC knows it came from the controller's node ID. const node = controller.nodes.get(msg.getNodeId()); // Create a lazy frame, so it can be deserialized on the node after a short delay to simulate radio transmission const lazyPayload = createLazySendDataPayload(controller, node, msg); const ackRequested = !!(msg.transmitOptions & TransmitOptions.ACK); const lazyFrame = createMockZWaveRequestFrame(lazyPayload, { ackRequested, }); const ackPromise = controller.sendToNode(node, lazyFrame); if (msg.callbackId !== 0) { // Put the controller into waiting state controller.state.set(MockControllerStateKeys.CommunicationState, MockControllerCommunicationState.WaitingForNode); // If an ACK is requested, wait for the ACK and notify the host let ack = true; if (ackRequested) { try { const ackResult = await ackPromise; ack = !!ackResult?.ack; } catch (e) { // We want to know when we're using a command in tests that cannot be decoded yet if (isZWaveError(e) && e.code === ZWaveErrorCodes .Deserialization_NotImplemented) { console.error(e.message); throw e; } // Treat all other errors as no response ack = false; } } controller.state.set(MockControllerStateKeys.CommunicationState, MockControllerCommunicationState.Idle); const cb = new SendDataBridgeRequestTransmitReport({ callbackId: msg.callbackId, transmitStatus: ack ? TransmitStatus.OK : TransmitStatus.NoAck, }); await controller.sendMessageToHost(cb); } else { // No callback was requested, we're done controller.state.set(MockControllerStateKeys.CommunicationState, MockControllerCommunicationState.Idle); } return true; } }, }; const handleSendDataMulticastBridge = { async onHostMessage(controller, msg) { if (msg instanceof SendDataMulticastBridgeRequest) { // Check if this command is legal right now const state = controller.state.get(MockControllerStateKeys.CommunicationState); if (state != undefined && state !== MockControllerCommunicationState.Idle) { throw new Error("Received SendDataMulticastBridgeRequest while not idle"); } // Put the controller into sending state controller.state.set(MockControllerStateKeys.CommunicationState, MockControllerCommunicationState.Sending); // Notify the host that the message was sent const res = new SendDataMulticastBridgeResponse({ wasSent: true, }); await controller.sendMessageToHost(res); // We deferred parsing of the CC because it requires the node's host to do so. // Now we can do that. Also set the CC node ID to the controller's own node ID, // so CC knows it came from the controller's node ID. const nodeIds = msg.nodeIds; const ackRequested = !!(msg.transmitOptions & TransmitOptions.ACK); const ackPromises = nodeIds.map((nodeId) => { const node = controller.nodes.get(nodeId); // Create a lazy frame, so it can be deserialized on the node after a short delay to simulate radio transmission const lazyPayload = createLazySendDataPayload(controller, node, msg); const lazyFrame = createMockZWaveRequestFrame(lazyPayload, { ackRequested, }); const ackPromise = controller.sendToNode(node, lazyFrame); return ackPromise; }); if (msg.callbackId !== 0) { // Put the controller into waiting state controller.state.set(MockControllerStateKeys.CommunicationState, MockControllerCommunicationState.WaitingForNode); // If an ACK is requested, wait for the ACKs and notify the host let ack = true; if (ackRequested) { try { const ackResults = await Promise.all(ackPromises); ack = ackResults.every((result) => !!result?.ack); } catch (e) { // We want to know when we're using a command in tests that cannot be decoded yet if (isZWaveError(e) && e.code === ZWaveErrorCodes .Deserialization_NotImplemented) { console.error(e.message); throw e; } // Treat all other errors as no response ack = false; } } controller.state.set(MockControllerStateKeys.CommunicationState, MockControllerCommunicationState.Idle); const cb = new SendDataMulticastBridgeRequestTransmitReport({ callbackId: msg.callbackId, transmitStatus: ack ? TransmitStatus.OK : TransmitStatus.NoAck, }); await controller.sendMessageToHost(cb); } else { // No callback was requested, we're done controller.state.set(MockControllerStateKeys.CommunicationState, MockControllerCommunicationState.Idle); } return true; } }, }; const handleRequestNodeInfo = { async onHostMessage(controller, msg) { if (msg instanceof RequestNodeInfoRequest) { // Check if this command is legal right now const state = controller.state.get(MockControllerStateKeys.CommunicationState); if (state != undefined && state !== MockControllerCommunicationState.Idle) { throw new Error("Received RequestNodeInfoRequest while not idle"); } // Put the controller into sending state controller.state.set(MockControllerStateKeys.CommunicationState, MockControllerCommunicationState.Sending); // Send the data to the node const node = controller.nodes.get(msg.getNodeId()); const command = new ZWaveProtocolCCRequestNodeInformationFrame({ nodeId: controller.ownNodeId, }); const frame = createMockZWaveRequestFrame(command, { ackRequested: false, }); void controller.sendToNode(node, frame); const nodeInfoPromise = controller.expectNodeCC(node, (cc) => cc instanceof ZWaveProtocolCCNodeInformationFrame, { timeout: MOCK_FRAME_ACK_TIMEOUT, // Prevent forwarding the NIF to the host. Otherwise it will mess with // the state tracking in the MockController. preventDefault: true, }); // Notify the host that the message was sent const res = new RequestNodeInfoResponse({ wasSent: true, }); await controller.sendMessageToHost(res); // Put the controller into waiting state controller.state.set(MockControllerStateKeys.CommunicationState, MockControllerCommunicationState.WaitingForNode); // Wait for node information and notify the host let cb; try { const nodeInfo = await nodeInfoPromise; cb = new ApplicationUpdateRequestNodeInfoReceived({ nodeInformation: { ...nodeInfo, nodeId: nodeInfo.nodeId, }, }); } catch { cb = new ApplicationUpdateRequestNodeInfoRequestFailed(); } controller.state.set(MockControllerStateKeys.CommunicationState, MockControllerCommunicationState.Idle); await controller.sendMessageToHost(cb); return true; } }, }; async function transmitSUCReturnRoute(controller, node, callbackId, options) { const expectCallback = callbackId !== 0; // Send the command to the node const command = new ZWaveProtocolCCAssignSUCReturnRoute({ nodeId: node.id, destinationNodeId: controller.ownNodeId, routeIndex: 0, // don't care ...options, }); const frame = createMockZWaveRequestFrame(command, { ackRequested: expectCallback, }); const ackPromise = controller.sendToNode(node, frame); let ack = false; if (expectCallback) { // Put the controller into waiting state controller.state.set(MockControllerStateKeys.CommunicationState, MockControllerCommunicationState.WaitingForNode); // Wait for the ACK and notify the host try { const ackResult = await ackPromise; ack = !!ackResult?.ack; } catch { // No response } } controller.state.set(MockControllerStateKeys.CommunicationState, MockControllerCommunicationState.Idle); return ack; } const handleDeleteSUCReturnRoute = { async onHostMessage(controller, msg) { if (msg instanceof DeleteSUCReturnRouteRequest) { // Check if this command is legal right now const state = controller.state.get(MockControllerStateKeys.CommunicationState); if (state != undefined && state !== MockControllerCommunicationState.Idle) { throw new Error("Received DeleteSUCReturnRouteRequest while not idle"); } // Put the controller into sending state controller.state.set(MockControllerStateKeys.CommunicationState, MockControllerCommunicationState.Sending); const expectCallback = msg.callbackId !== 0; // Send the command to the node const node = controller.nodes.get(msg.getNodeId()); // Respond with success const response = new DeleteSUCReturnRouteResponse({ wasExecuted: true, }); await controller.sendMessageToHost(response); const ack = await transmitSUCReturnRoute(controller, node, msg.callbackId ?? 0, // Set the empty route { destinationSpeed: ZWaveDataRate["9k6"], destinationWakeUp: WakeUpTime.None, repeaters: [], }); if (expectCallback) { const transmitReport = new DeleteSUCReturnRouteRequestTransmitReport({ callbackId: msg.callbackId, transmitStatus: ack ? TransmitStatus.OK : TransmitStatus.NoAck, }); await controller.sendMessageToHost(transmitReport); } return true; } }, }; const handleAssignSUCReturnRoute = { async onHostMessage(controller, msg) { if (msg instanceof AssignSUCReturnRouteRequest) { // Check if this command is legal right now const state = controller.state.get(MockControllerStateKeys.CommunicationState); if (state != undefined && state !== MockControllerCommunicationState.Idle) { throw new Error("Received AssignSUCReturnRouteRequest while not idle"); } // Put the controller into sending state controller.state.set(MockControllerStateKeys.CommunicationState, MockControllerCommunicationState.Sending); const expectCallback = msg.callbackId !== 0; // Send the command to the node const node = controller.nodes.get(msg.getNodeId()); // Notify the host that the message was sent const res = new AssignSUCReturnRouteResponse({ wasExecuted: true, }); await controller.sendMessageToHost(res); // Assign uses 100kbps speed and empty repeaters (supports beaming) const ack = await transmitSUCReturnRoute(controller, node, msg.callbackId ?? 0, // Don't care about the actual settings here { destinationSpeed: ZWaveDataRate["100k"], destinationWakeUp: WakeUpTime.None, repeaters: [], }); if (expectCallback) { const cb = new AssignSUCReturnRouteRequestTransmitReport({ callbackId: msg.callbackId, transmitStatus: ack ? TransmitStatus.OK : TransmitStatus.NoAck, }); await controller.sendMessageToHost(cb); } return true; } }, }; const handleAddNode = { async onHostMessage(controller, msg) { if (msg instanceof AddNodeToNetworkRequest) { // Check if this command is legal right now const state = controller.state.get(MockControllerStateKeys.InclusionState); const expectCallback = msg.callbackId !== 0; let cb; if (state === MockControllerInclusionState.AddingNode) { // While adding, only accept stop commands if (msg.addNodeType === AddNodeType.Stop) { controller.state.set(MockControllerStateKeys.InclusionState, MockControllerInclusionState.Idle); // If there's a node pending inclusion, send Done status with node ID const pendingNode = controller.nodePendingInclusion; if (pendingNode) { cb = new AddNodeToNetworkRequestStatusReport({ callbackId: msg.callbackId, status: AddNodeStatus.Done, nodeId: pendingNode.id, }); // Clear the pending node controller.nodePendingInclusion = undefined; } else { cb = new AddNodeToNetworkRequestStatusReport({ callbackId: msg.callbackId, status: AddNodeStatus.Failed, }); } } else { cb = new AddNodeToNetworkRequestStatusReport({ callbackId: msg.callbackId, status: AddNodeStatus.Failed, }); } } else if (state === MockControllerInclusionState.RemovingNode) { // Cannot start adding nodes while removing one cb = new AddNodeToNetworkRequestStatusReport({ callbackId: msg.callbackId, status: AddNodeStatus.Failed, }); } else { // Idle // Set the controller into "adding node" state controller.state.set(MockControllerStateKeys.InclusionState, MockControllerInclusionState.AddingNode); cb = new AddNodeToNetworkRequestStatusReport({ callbackId: msg.callbackId, status: AddNodeStatus.Ready, }); // If there's a node pending inclusion, simulate the inclusion sequence // after responding to the add request if (controller.nodePendingInclusion) { const { setup: testSpecificSetup, ...nodeOptions } = controller.nodePendingInclusion; const node = await MockNode.create({ controller, ...nodeOptions, }); // Apply default behaviors that are required for interacting with the driver correctly node.defineBehavior(...createDefaultMockNodeBehaviors()); // Allow the tests to set up additional behavior before inclusion happens testSpecificSetup?.(node); const supportedCCs = [...node.implementedCCs] .filter(([, info]) => info.isSupported && info.version > 0) .map(([cc]) => cc); const nodeInfo = { nodeId: node.id, basicDeviceClass: node.capabilities.basicDeviceClass, genericDeviceClass: node.capabilities.genericDeviceClass, specificDeviceClass: node.capabilities.specificDeviceClass, supportedCCs, }; void (async () => { // Wait a bit, then send NodeFound await wait(10); const nodeFoundMsg = new AddNodeToNetworkRequestStatusReport({ callbackId: msg.callbackId, status: AddNodeStatus.NodeFound, }); await controller.sendMessageToHost(nodeFoundMsg); // Wait a bit, then send AddingSlave with node info await wait(10); const addingSlaveMsg = new AddNodeToNetworkRequestStatusReport({ callbackId: msg.callbackId, status: AddNodeStatus.AddingSlave, nodeInfo, }); await controller.sendMessageToHost(addingSlaveMsg); // Wait a bit, add the node to the controller's list, then send ProtocolDone await wait(10); controller.addNode(node); const protocolDoneMsg = new AddNodeToNetworkRequestStatusReport({ callbackId: msg.callbackId, status: AddNodeStatus.ProtocolDone, }); await controller.sendMessageToHost(protocolDoneMsg); })(); } } if (expectCallback && cb) { await controller.sendMessageToHost(cb); } return true; } }, }; const handleRemoveNode = { async onHostMessage(controller, msg) { if (msg instanceof RemoveNodeFromNetworkRequest) { // Check if this command is legal right now const state = controller.state.get(MockControllerStateKeys.InclusionState); const expectCallback = msg.callbackId !== 0; let cb; if (state === MockControllerInclusionState.RemovingNode) { // While removing, only accept stop commands if (msg.removeNodeType === RemoveNodeType.Stop) { controller.state.set(MockControllerStateKeys.InclusionState, MockControllerInclusionState.Idle); cb = new RemoveNodeFromNetworkRequestStatusReport({ callbackId: msg.callbackId, status: RemoveNodeStatus.Failed, }); } else { cb = new RemoveNodeFromNetworkRequestStatusReport({ callbackId: msg.callbackId, status: RemoveNodeStatus.Failed, }); } } else if (state === MockControllerInclusionState.AddingNode) { // Cannot start removing nodes while adding one cb = new RemoveNodeFromNetworkRequestStatusReport({ callbackId: msg.callbackId, status: RemoveNodeStatus.Failed, }); } else { // Idle // Set the controller into "removing node" state // For now we don't actually do anything in that state controller.state.set(MockControllerStateKeys.InclusionState, MockControllerInclusionState.RemovingNode); cb = new RemoveNodeFromNetworkRequestStatusReport({ callbackId: msg.callbackId, status: RemoveNodeStatus.Ready, }); } if (expectCallback && cb) { await controller.sendMessageToHost(cb); } return true; } }, }; const forwardCommandClassesToHost = { async onNodeFrame(controller, node, frame) { if (frame.type === MockZWaveFrameType.Request && frame.payload instanceof CommandClass && !(frame.payload instanceof ZWaveProtocolCC)) { // This is a CC that is meant for the host application const msg = new ApplicationCommandRequest({ command: frame.payload, }); // Nodes send commands TO the controller, so we need to fix the node ID before forwarding msg.getNodeId = () => node.id; // Simulate a serialized frame being transmitted via radio before receiving it await controller.sendMessageToHost(msg, node); return true; } }, }; const forwardUnsolicitedNIF = { async onNodeFrame(controller, node, frame) { if (frame.type === MockZWaveFrameType.Request && frame.payload instanceof ZWaveProtocolCCNodeInformationFrame) { const updateRequest = new ApplicationUpdateRequestNodeInfoReceived({ nodeInformation: { ...frame.payload, nodeId: frame.payload.nodeId, }, }); // Simulate a serialized frame being transmitted via radio before receiving it await controller.sendMessageToHost(updateRequest, node); return true; } }, }; /** Predefined default behaviors that are required for interacting with the driver correctly */ export function createDefaultBehaviors() { return [ respondToGetControllerId, respondToGetSerialApiCapabilities, respondToGetControllerVersion, respondToGetControllerCapabilities, respondToGetSUCNodeId, respondToGetSerialApiInitData, respondToSoftReset, respondToGetNodeProtocolInfo, handleSendData, handleSendDataMulticast, handleSendDataBridge, handleSendDataMulticastBridge, handleRequestNodeInfo, handleDeleteSUCReturnRoute, handleAssignSUCReturnRoute, handleAddNode, handleRemoveNode, forwardCommandClassesToHost, forwardUnsolicitedNIF, ]; } //# sourceMappingURL=MockControllerBehaviors.js.map