@ljames8/hormann-hcp-client
Version:
Hormann Communication Protocol v1 garage door serial client
180 lines (179 loc) • 7.91 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.HormannGarageDoorOpener = exports.TargetDoorState = exports.CurrentDoorState = exports.SerialHCPClient = exports.MockHCPClient = void 0;
exports.createHormannGarageDoorOpener = createHormannGarageDoorOpener;
const node_events_1 = require("node:events");
const debug_1 = __importDefault(require("debug"));
const serialHCPClient_1 = require("./serialHCPClient");
Object.defineProperty(exports, "SerialHCPClient", { enumerable: true, get: function () { return serialHCPClient_1.SerialHCPClient; } });
var mockHCPClient_1 = require("./mockHCPClient");
Object.defineProperty(exports, "MockHCPClient", { enumerable: true, get: function () { return mockHCPClient_1.MockHCPClient; } });
var CurrentDoorState;
(function (CurrentDoorState) {
CurrentDoorState[CurrentDoorState["OPEN"] = 0] = "OPEN";
CurrentDoorState[CurrentDoorState["CLOSED"] = 1] = "CLOSED";
CurrentDoorState[CurrentDoorState["OPENING"] = 2] = "OPENING";
CurrentDoorState[CurrentDoorState["CLOSING"] = 3] = "CLOSING";
CurrentDoorState[CurrentDoorState["STOPPED"] = 4] = "STOPPED";
CurrentDoorState[CurrentDoorState["VENTING"] = 5] = "VENTING";
})(CurrentDoorState || (exports.CurrentDoorState = CurrentDoorState = {}));
exports.TargetDoorState = {
OPEN: CurrentDoorState.OPEN,
CLOSED: CurrentDoorState.CLOSED,
VENTING: CurrentDoorState.VENTING,
};
class GarageDoorOpener extends node_events_1.EventEmitter {
name;
manufacturer;
model;
currentState = null;
targetState = null;
lightState = null;
constructor(name) {
super();
this.name = name;
}
}
class HormannGarageDoorOpener extends GarageDoorOpener {
hcpClient;
logger;
broadcastStatus;
constructor(name = "Hörmann Garage Door", hcpClient) {
super(name);
this.hcpClient = hcpClient;
this.logger = (0, debug_1.default)(`door:${this.name}`);
this.manufacturer = "Hörmann";
this.model = "Supramatic E3";
this.broadcastStatus = new Uint8Array(2);
this.hcpClient.on("data", this.onBroadcast.bind(this));
}
static targetStateToRequest(targetState) {
switch (targetState) {
case exports.TargetDoorState.OPEN: {
return { flags: [serialHCPClient_1.STATUS_RESPONSE_BYTE0_BITFIELD.OPEN] };
}
case exports.TargetDoorState.CLOSED: {
return { flags: [serialHCPClient_1.STATUS_RESPONSE_BYTE0_BITFIELD.CLOSE] };
}
case exports.TargetDoorState.VENTING: {
return { flags: [serialHCPClient_1.STATUS_RESPONSE_BYTE0_BITFIELD.VENTING] };
}
}
}
static broadcastToCurrentState(status) {
const bitField = serialHCPClient_1.SerialHCPClient.extractBitfield(status[0]);
if (bitField[serialHCPClient_1.BROADCAST_STATUS_BYTE0_BITFIELD.ERROR_ACTIVE] === true) {
return new Error("Error active");
}
const lightState = bitField[serialHCPClient_1.BROADCAST_STATUS_BYTE0_BITFIELD.LIGHT_RELAY_ON];
if (bitField[serialHCPClient_1.BROADCAST_STATUS_BYTE0_BITFIELD.DOOR_MOVING] === true) {
switch (bitField[serialHCPClient_1.BROADCAST_STATUS_BYTE0_BITFIELD.DOOR_DIRECTION]) {
case Boolean(serialHCPClient_1.DIRECTION.OPENING):
return { door: CurrentDoorState.OPENING, light: lightState };
case Boolean(serialHCPClient_1.DIRECTION.CLOSING):
return { door: CurrentDoorState.CLOSING, light: lightState };
}
}
// if not moving and no error
if (bitField[serialHCPClient_1.BROADCAST_STATUS_BYTE0_BITFIELD.DOOR_OPENED] === true) {
return { door: CurrentDoorState.OPEN, light: lightState };
}
else if (bitField[serialHCPClient_1.BROADCAST_STATUS_BYTE0_BITFIELD.DOOR_CLOSED] === true) {
return { door: CurrentDoorState.CLOSED, light: lightState };
}
else if (bitField[serialHCPClient_1.BROADCAST_STATUS_BYTE0_BITFIELD.DOOR_VENTING] === true) {
return { door: CurrentDoorState.VENTING, light: lightState };
}
else {
return new Error("Unknown status");
}
}
onBroadcast(status) {
// client 'data' callback, for each broadcast status update
if (status[0] != this.broadcastStatus[0]) {
// consider only byte 0 as info from byte 1 unknown
this.broadcastStatus = status;
const newState = HormannGarageDoorOpener.broadcastToCurrentState(status);
if (newState instanceof Error) {
this.emit("error", newState);
// TODO: set current state as STOPPED ?
}
else {
if (this.currentState != newState.door) {
this.currentState = newState.door;
this.logger(`Current door state now ${CurrentDoorState[this.currentState]}`);
this.emit("update_door", newState.door);
}
if (this.lightState != newState.light) {
this.lightState = newState.light;
this.logger(`Current light state now ${this.lightState}`);
this.emit("update_light", newState.light);
}
}
}
}
getCurrentState() {
if (this.currentState === null) {
throw new Error("Current state cannot be retrieved");
}
else {
return this.currentState;
}
}
getTargetState() {
if (this.targetState === null) {
throw new Error("Target state is not set");
}
else {
return this.targetState;
}
}
setTargetState(newState) {
if (this.targetState === newState) {
this.logger(`Target state already ${CurrentDoorState[this.targetState]}(${newState})`);
return Promise.resolve();
}
else if (this.currentState === newState) {
this.logger(`Current state already ${CurrentDoorState[this.currentState]}(${newState})`);
this.targetState = newState;
return Promise.resolve();
}
else {
// ask client to operate door
const { flags, emergencyStop } = HormannGarageDoorOpener.targetStateToRequest(newState);
return this.hcpClient.pushCommand(flags, emergencyStop).then(() => {
this.targetState = newState;
this.logger(`Target state set to ${CurrentDoorState[this.targetState]}`);
});
}
}
getLightOnState() {
if (this.lightState === null) {
throw new Error("Light state is not set");
}
else {
return this.lightState;
}
}
setLightOnState(newState) {
if (this.lightState === newState) {
this.logger(`Light On state already ${newState}`);
return Promise.resolve();
}
else {
// ask client to toggle light
return this.hcpClient
.pushCommand([serialHCPClient_1.STATUS_RESPONSE_BYTE0_BITFIELD.TOGGLE_LIGHT], false)
.then(() => { });
}
}
}
exports.HormannGarageDoorOpener = HormannGarageDoorOpener;
function createHormannGarageDoorOpener(
/** factory function to create a serial enabled Hormann garage door opener */
name, { path, ...rest }, { packetTimeout = 50, filterBreaks = true, filterMaxLength = true } = {}, listenOnly = false) {
return new HormannGarageDoorOpener(name, new serialHCPClient_1.SerialHCPClient({ path, ...rest }, { packetTimeout, filterBreaks, filterMaxLength }, listenOnly));
}