UNPKG

@ljames8/hormann-hcp-client

Version:

Hormann Communication Protocol v1 garage door serial client

131 lines (130 loc) 5.92 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.MockHCPClient = void 0; const node_events_1 = require("node:events"); const garageDoor_1 = require("./garageDoor"); const serialHCPClient_1 = require("./serialHCPClient"); const parser_1 = require("./parser"); // TODO: write some doc class MockHCPClient extends node_events_1.EventEmitter { pushCommandMock; listenOnly; // keeping track of the parent garageState can be useful mockState = { door: garageDoor_1.CurrentDoorState.CLOSED, light: false }; constructor(pushCommandMock = () => new Promise((resolve) => { // just a dummy packet return after 30ms setTimeout(() => { resolve(parser_1.HCPPacket.fromData(0x80, 0x01, [0xff, 0xff])); }, 30); }), listenOnly = false) { super(); this.pushCommandMock = pushCommandMock; this.listenOnly = listenOnly; } inferPushCommandMock(flags, emergencyStop) { /** smarter pushCommandMock method that infers what to emit based on command and mockState */ let callback = null; if (emergencyStop === true) { callback = () => { this.emitDoorState(garageDoor_1.CurrentDoorState.STOPPED); }; } else { // infer next state const nextState = MockHCPClient.responseStatusToNextState(flags, this.mockState); callback = () => { this.emitGarageState(nextState); }; } return new Promise((resolve) => { // TODO: parametrize timeout values // confirm command received after 30ms setTimeout(() => { resolve(parser_1.HCPPacket.fromData(0x80, 0x01, [0xff, 0xff])); // emit next door state after 100ms setTimeout(callback, 100); }, 30); }); } pushCommand(flags, emergencyStop = false) { if (this.listenOnly) { return Promise.reject(new Error("Cannot send commands in listen-only mode")); } return this.pushCommandMock(flags, emergencyStop); } static doorStateToBroadcastStatusByte(state) { switch (state) { case garageDoor_1.CurrentDoorState.CLOSED: return 1 << serialHCPClient_1.BROADCAST_STATUS_BYTE0_BITFIELD.DOOR_CLOSED; case garageDoor_1.CurrentDoorState.OPEN: return 1 << serialHCPClient_1.BROADCAST_STATUS_BYTE0_BITFIELD.DOOR_OPENED; case garageDoor_1.CurrentDoorState.CLOSING: { // set direction return ((1 << serialHCPClient_1.BROADCAST_STATUS_BYTE0_BITFIELD.DOOR_MOVING) | ((1 << serialHCPClient_1.BROADCAST_STATUS_BYTE0_BITFIELD.DOOR_DIRECTION) * serialHCPClient_1.DIRECTION.CLOSING)); } case garageDoor_1.CurrentDoorState.OPENING: { // set direction return ((1 << serialHCPClient_1.BROADCAST_STATUS_BYTE0_BITFIELD.DOOR_MOVING) | ((1 << serialHCPClient_1.BROADCAST_STATUS_BYTE0_BITFIELD.DOOR_DIRECTION) * serialHCPClient_1.DIRECTION.OPENING)); } case garageDoor_1.CurrentDoorState.VENTING: return 1 << serialHCPClient_1.BROADCAST_STATUS_BYTE0_BITFIELD.DOOR_VENTING; case garageDoor_1.CurrentDoorState.STOPPED: return 1 << serialHCPClient_1.BROADCAST_STATUS_BYTE0_BITFIELD.ERROR_ACTIVE; } } static lightStateToBroadcastStatusByte(state) { return state === true ? 1 << serialHCPClient_1.BROADCAST_STATUS_BYTE0_BITFIELD.LIGHT_RELAY_ON : 0; } static garageStateToBroadcastStatus(state) { const status = new Uint8Array(2); // set arbitrary 2nd status byte value status[1] = 0xff; status[0] = MockHCPClient.doorStateToBroadcastStatusByte(state.door); status[0] |= MockHCPClient.lightStateToBroadcastStatusByte(state.light); return status; } static responseStatusToNextState(flags, currentState) { /** guess logical next state from input command and current state */ const nextState = { ...currentState }; if (flags.includes(serialHCPClient_1.STATUS_RESPONSE_BYTE0_BITFIELD.TOGGLE_LIGHT)) { nextState.light = !currentState.light; } if (flags.includes(serialHCPClient_1.STATUS_RESPONSE_BYTE0_BITFIELD.VENTING)) { nextState.door = garageDoor_1.CurrentDoorState.VENTING; } else if (flags.includes(serialHCPClient_1.STATUS_RESPONSE_BYTE0_BITFIELD.CLOSE)) { nextState.door = currentState.door === garageDoor_1.CurrentDoorState.CLOSED ? garageDoor_1.CurrentDoorState.CLOSED : garageDoor_1.CurrentDoorState.CLOSING; } else if (flags.includes(serialHCPClient_1.STATUS_RESPONSE_BYTE0_BITFIELD.OPEN)) { nextState.door = currentState.door === garageDoor_1.CurrentDoorState.OPEN ? garageDoor_1.CurrentDoorState.OPEN : garageDoor_1.CurrentDoorState.OPENING; } return nextState; } emitGarageState(state) { const nextState = MockHCPClient.garageStateToBroadcastStatus(state); const success = this.emit("data", nextState); if (success === true) this.mockState = state; return success; } emitDoorError() { const status = new Uint8Array(2); status[0] = 1 << serialHCPClient_1.BROADCAST_STATUS_BYTE0_BITFIELD.ERROR_ACTIVE; return this.emit("data", status); } emitDoorState(state) { return this.emitGarageState({ door: state, light: this.mockState.light }); } emitLightState(state) { return this.emitGarageState({ door: this.mockState.door, light: state }); } } exports.MockHCPClient = MockHCPClient;