UNPKG

inventoresed

Version:

Z-Wave driver written entirely in JavaScript/TypeScript

226 lines (197 loc) 7.01 kB
import type { BinarySensorCCAPI } from "@zwave-js/cc/BinarySensorCC"; import { BinarySwitchCCAPI } from "@zwave-js/cc/BinarySwitchCC"; import { assertZWaveError, CommandClasses, ZWaveErrorCodes, } from "@zwave-js/core"; import type { MockSerialPort } from "@zwave-js/serial"; import { FunctionType } from "@zwave-js/serial"; import type { ThrowingMap } from "@zwave-js/shared"; import { wait } from "alcalzone-shared/async"; import { ZWaveController } from "../controller/Controller"; import type { Driver } from "../driver/Driver"; import { createAndStartDriver } from "../test/utils"; import { ZWaveNode } from "./Node"; // Test mock for isFunctionSupported to control which commands are getting used function isFunctionSupported(fn: FunctionType): boolean { switch (fn) { case FunctionType.SendDataBridge: case FunctionType.SendDataMulticastBridge: return false; } return true; } describe("lib/node/VirtualEndpoint", () => { let driver: Driver; let serialport: MockSerialPort; beforeEach(async () => { ({ driver, serialport } = await createAndStartDriver()); driver["_controller"] = new ZWaveController(driver); driver["_controller"].isFunctionSupported = isFunctionSupported; }); function makePhysicalNode(nodeId: number): ZWaveNode { const node = new ZWaveNode(nodeId, driver); (driver.controller.nodes as ThrowingMap<number, ZWaveNode>).set( nodeId, node, ); return node; } // function setNumEndpoints(node: ZWaveNode, numEndpoints: number) { // node.valueDB.setValue( // { // commandClass: CommandClasses["Multi Channel"], // property: "individualCount", // }, // numEndpoints, // ); // } afterEach(async () => { await driver.destroy(); driver.removeAllListeners(); }); describe("createAPI", () => { it("throws if a non-implemented API should be created", () => { const broadcast = driver.controller.getBroadcastNode(); assertZWaveError(() => broadcast.createAPI(0xbada55), { errorCode: ZWaveErrorCodes.CC_NoAPI, messageMatches: "no associated API", }); }); it("the broadcast API throws when trying to access a non-supported CC", async () => { makePhysicalNode(2); makePhysicalNode(3); const broadcast = driver.controller.getBroadcastNode(); // We must not use Basic CC here, because that is assumed to be always supported const api = broadcast.createAPI( CommandClasses["Binary Switch"], ) as BinarySensorCCAPI; // this does not throw api.isSupported(); // this does await assertZWaveError(() => api.get(), { errorCode: ZWaveErrorCodes.CC_NotSupported, }); }); it("the broadcast API should know it is a broadcast API", async () => { makePhysicalNode(2); makePhysicalNode(3); const broadcast = driver.controller.getBroadcastNode(); expect( broadcast.createAPI(CommandClasses.Basic)["isBroadcast"](), ).toBeTrue(); }); it("the multicast API should know it is a multicast API", async () => { makePhysicalNode(2); makePhysicalNode(3); const multicast = driver.controller.getMulticastGroup([2, 3]); expect( multicast.createAPI(CommandClasses.Basic)["isMulticast"](), ).toBeTrue(); }); }); describe("commandClasses dictionary", () => { let node2: ZWaveNode; let node3: ZWaveNode; beforeEach(() => { node2 = makePhysicalNode(2); node3 = makePhysicalNode(3); }); it("throws when trying to access a non-implemented CC", () => { const broadcast = driver.controller.getBroadcastNode(); assertZWaveError(() => (broadcast.commandClasses as any).FOOBAR, { errorCode: ZWaveErrorCodes.CC_NotImplemented, messageMatches: "FOOBAR is not implemented", }); }); it("throws when trying to use a command of an unsupported CC", () => { const broadcast = driver.controller.getBroadcastNode(); assertZWaveError( () => broadcast.commandClasses["Binary Switch"].set(true), { errorCode: ZWaveErrorCodes.CC_NotSupported, messageMatches: "does not support the Command Class Binary Switch", }, ); }); it("does not throw when checking support of a CC", () => { const broadcast = driver.controller.getBroadcastNode(); expect( broadcast.commandClasses["Binary Switch"].isSupported(), ).toBeFalse(); }); it("does not throw when accessing the ID of a CC", () => { const broadcast = driver.controller.getBroadcastNode(); expect(broadcast.commandClasses["Binary Switch"].ccId).toBe( CommandClasses["Binary Switch"], ); }); it("does not throw when scoping the API options", () => { const broadcast = driver.controller.getBroadcastNode(); broadcast.commandClasses["Binary Switch"].withOptions({}); }); it("returns all supported CCs when being enumerated", () => { // No supported CCs, empty array let broadcast = driver.controller.getBroadcastNode(); let actual = [...broadcast.commandClasses]; expect(actual).toEqual([]); // Supported and controlled CCs node2.addCC(CommandClasses["Binary Switch"], { isSupported: true }); node2.addCC(CommandClasses["Wake Up"], { isControlled: true }); node3.addCC(CommandClasses["Binary Switch"], { isSupported: true }); node3.addCC(CommandClasses.Version, { isSupported: true }); broadcast = driver.controller.getBroadcastNode(); actual = [...broadcast.commandClasses]; expect(actual).toHaveLength(1); expect(actual.map((api) => api.constructor)).toIncludeAllMembers([ BinarySwitchCCAPI, // VersionCCAPI cannot be used in broadcast // WakeUpCCAPI is not supported (only controlled), so no API! ]); }); it("returns [object Object] when turned into a string", () => { const broadcast = driver.controller.getBroadcastNode(); expect((broadcast.commandClasses as any)[Symbol.toStringTag]).toBe( "[object Object]", ); }); it("returns undefined for other symbol properties", () => { const broadcast = driver.controller.getBroadcastNode(); expect( (broadcast.commandClasses as any)[Symbol.unscopables], ).toBeUndefined(); }); }); describe("uses the correct commands behind the scenes", () => { it("broadcast", async () => { makePhysicalNode(2); makePhysicalNode(3); const broadcast = driver.controller.getBroadcastNode(); broadcast.commandClasses.Basic.set(99); await wait(1); // » [Node 255] [REQ] [SendData] // │ transmit options: 0x25 // │ callback id: 1 // └─[BasicCCSet] expect(serialport.lastWrite).toEqual( Buffer.from("010a0013ff0320016325017c", "hex"), ); }); it("multicast", async () => { makePhysicalNode(2); makePhysicalNode(3); const multicast = driver.controller.getMulticastGroup([2, 3]); multicast.commandClasses.Basic.set(99); await wait(1); // » [Node 2, 3] [REQ] [SendData] // │ transmit options: 0x25 // │ callback id: 1 // └─[BasicCCSet] expect(serialport.lastWrite).toEqual( Buffer.from("010c001402020303200163250181", "hex"), ); }); }); });