homebridge-virtual-accessories
Version:
Virtual HomeKit accessories for Homebridge.
162 lines • 9.24 kB
JavaScript
/* eslint-disable brace-style */
/* eslint-disable max-len */
import { Accessory } from './accessory.js';
import { InvalidObstructionValueType, ObstructionValueUpdateNotAllowed } from '../errors.js';
/**
* GarageDoor - Accessory implementation
*/
export class GarageDoor extends Accessory {
static ACCESSORY_TYPE_NAME = 'GarageDoor';
static OPEN = 0; // Characteristic.CurrentDoorState.OPEN - Characteristic.TargetDoorState.OPEN
static CLOSED = 1; // Characteristic.CurrentDoorState.CLOSED - Characteristic.TargetDoorState.CLOSED
static OPENING = 2; // Characteristic.CurrentDoorState.OPENING
static CLOSING = 3; // Characteristic.CurrentDoorState.CLOSING
static STOPPED = 4; // Characteristic.CurrentDoorState.STOPPED
static DEFAULT_TIMEOUT_SECS = 3;
stateStorageKey = 'GarageDoorState';
transitionTimerId;
states = {
GarageDoorCurrentState: GarageDoor.CLOSED,
GarageDoorTargetState: GarageDoor.CLOSED,
ObstructionDetected: false,
};
constructor(platform, accessory, accessoryConfiguration) {
super(platform, accessory, accessoryConfiguration);
// First configure the device based on the accessory details
this.defaultState = this.accessoryConfiguration.garageDoor.defaultState === 'open' ? GarageDoor.OPEN : GarageDoor.CLOSED;
this.states.GarageDoorCurrentState = this.defaultState;
// If the accessory is stateful retrieve stored state
if (this.accessoryConfiguration.accessoryIsStateful) {
const accessoryState = this.loadAccessoryState(this.storagePath);
const cachedState = accessoryState[this.stateStorageKey];
if (cachedState !== undefined) {
this.states.GarageDoorCurrentState = cachedState;
}
}
this.states.GarageDoorTargetState = this.states.GarageDoorCurrentState;
this.service = this.accessory.getService(this.platform.Service.GarageDoorOpener) || this.accessory.addService(this.platform.Service.GarageDoorOpener);
this.service.setCharacteristic(this.platform.Characteristic.Name, this.accessoryConfiguration.accessoryName);
// Update the initial state of the accessory
this.log.debug(`[${this.accessoryConfiguration.accessoryName}] Setting Garage Door Current State: ${GarageDoor.getStateName(this.states.GarageDoorCurrentState)}`);
this.service.updateCharacteristic(this.platform.Characteristic.CurrentDoorState, (this.states.GarageDoorCurrentState));
this.service.updateCharacteristic(this.platform.Characteristic.TargetDoorState, (this.states.GarageDoorTargetState));
// register handlers
this.service.getCharacteristic(this.platform.Characteristic.CurrentDoorState)
.onGet(this.getCurrentDoorState.bind(this));
this.service.getCharacteristic(this.platform.Characteristic.TargetDoorState)
.onSet(this.setTargetDoorState.bind(this))
.onGet(this.getTargetDoorState.bind(this));
this.service.getCharacteristic(this.platform.Characteristic.ObstructionDetected)
.onGet(this.getObstructionDetected.bind(this));
}
// Handlers
async getCurrentDoorState() {
const garageDoorCurrentState = this.states.GarageDoorCurrentState;
this.log.debug(`[${this.accessoryConfiguration.accessoryName}] Getting Current Door State: ${GarageDoor.getStateName(garageDoorCurrentState)}`);
return garageDoorCurrentState;
}
async setTargetDoorState(value) {
this.states.GarageDoorTargetState = value;
this.log.info(`[${this.accessoryConfiguration.accessoryName}] Setting Target Door State: ${GarageDoor.getStateName(this.states.GarageDoorTargetState)}`);
// Clear the obstruction flag
this.states.ObstructionDetected = false;
this.service.setCharacteristic(this.platform.Characteristic.ObstructionDetected, (this.states.ObstructionDetected));
this.log.debug(`[${this.accessoryConfiguration.accessoryName}] Setting Obstruction Detected: ${this.states.ObstructionDetected}`);
// CurrentDoorState CLOSING/OPENING
this.states.GarageDoorCurrentState = (this.states.GarageDoorTargetState === GarageDoor.OPEN) ? GarageDoor.OPENING : GarageDoor.CLOSING;
this.service.setCharacteristic(this.platform.Characteristic.CurrentDoorState, (this.states.GarageDoorCurrentState));
this.log.info(`[${this.accessoryConfiguration.accessoryName}] Setting Current Door State: ${GarageDoor.getStateName(this.states.GarageDoorCurrentState)}`);
// CurrentDoorState CLOSED/OPEN with 3 second delay
const transitionDuration = this.accessoryConfiguration.garageDoor.transitionDuration;
const transitionDelayMillis = (transitionDuration ? transitionDuration : GarageDoor.DEFAULT_TIMEOUT_SECS) * 1000;
// Reset timer, if running
clearTimeout(this.transitionTimerId);
this.transitionTimerId = setTimeout(() => {
this.states.GarageDoorCurrentState = this.states.GarageDoorTargetState;
this.service.setCharacteristic(this.platform.Characteristic.CurrentDoorState, (this.states.GarageDoorCurrentState));
this.storeState();
this.log.info(`[${this.accessoryConfiguration.accessoryName}] Setting Current Door State: ${GarageDoor.getStateName(this.states.GarageDoorCurrentState)}`);
}, transitionDelayMillis);
}
async getTargetDoorState() {
const garageDoorTargetState = this.states.GarageDoorTargetState;
this.log.debug(`[${this.accessoryConfiguration.accessoryName}] Getting Target Door State: ${GarageDoor.getStateName(garageDoorTargetState)}`);
return garageDoorTargetState;
}
async getObstructionDetected() {
const obstructionDetected = this.states.ObstructionDetected;
this.log.debug(`[${this.accessoryConfiguration.accessoryName}] Getting Obstruction Detected: ${obstructionDetected}`);
return obstructionDetected;
}
getJsonState() {
const json = JSON.stringify({
[this.stateStorageKey]: this.states.GarageDoorCurrentState,
});
return json;
}
getAccessoryTypeName() {
return GarageDoor.ACCESSORY_TYPE_NAME;
}
static getStateName(state) {
let stateName;
switch (state) {
case undefined: {
stateName = 'undefined';
break;
}
case GarageDoor.OPEN: {
stateName = 'OPEN';
break;
}
case GarageDoor.CLOSED: {
stateName = 'CLOSED';
break;
}
case GarageDoor.OPENING: {
stateName = 'OPENING';
break;
}
case GarageDoor.CLOSING: {
stateName = 'CLOSING';
break;
}
case GarageDoor.STOPPED: {
stateName = 'STOPPED';
break;
}
default: {
stateName = state.toString();
}
}
return stateName;
}
// Updatable Obstruction interface
updateObstruction(value, accessoryId) {
this.log.debug(`[${this.accessoryConfiguration.accessoryName}] Request update obstruction detected to ${value}`);
if (accessoryId !== this.accessoryConfiguration.accessoryID) {
this.log.error(`[${this.accessoryConfiguration.accessoryName}] Accessory Id ${accessoryId} is not valid for this accessory`);
throw new ObstructionValueUpdateNotAllowed(`Invalid accessory id: ${accessoryId}`);
}
else if (typeof value !== 'boolean') {
this.log.error(`[${this.accessoryConfiguration.accessoryName}] Value ${value} is not valid for Garage Door obstruction detected`);
throw new InvalidObstructionValueType(`Invalid sensor value: ${value}`);
}
else if (this.states.GarageDoorCurrentState === GarageDoor.OPENING || this.states.GarageDoorCurrentState === GarageDoor.CLOSING) {
this.states.ObstructionDetected = value;
this.service.setCharacteristic(this.platform.Characteristic.ObstructionDetected, (this.states.ObstructionDetected));
// Only log to 'info' if setting ObstructionDetected to true
this.log.debug(`[${this.accessoryConfiguration.accessoryName}] Updating obstruction detected to ${value}`);
if (value === true) {
// Reset timer, if running
clearTimeout(this.transitionTimerId);
this.states.GarageDoorCurrentState = GarageDoor.STOPPED;
this.service.setCharacteristic(this.platform.Characteristic.CurrentDoorState, (this.states.GarageDoorCurrentState));
this.log.info(`[${this.accessoryConfiguration.accessoryName}] Setting Current Door State: ${GarageDoor.getStateName(this.states.GarageDoorCurrentState)}`);
}
}
else {
this.log.debug(`[${this.accessoryConfiguration.accessoryName}] Ignoring. Garage Door is ${GarageDoor.getStateName(this.states.GarageDoorCurrentState)}`);
}
}
}
//# sourceMappingURL=virtualAccessoryGarageDoor.js.map