From fc55f7469b5c6eb99bc4e43a4e3156e3be057310 Mon Sep 17 00:00:00 2001 From: Just Jam Date: Sun, 24 Nov 2024 11:01:37 +0000 Subject: [PATCH] Added interrupt and reverse of opening/closing state --- package-lock.json | 4 +- package.json | 2 +- src/accessories/virtualAccessoryGarageDoor.ts | 68 +++++++++++-------- .../virtualAccessoryWindowCovering.ts | 41 ++++++----- 4 files changed, 65 insertions(+), 50 deletions(-) diff --git a/package-lock.json b/package-lock.json index f884e93..4c80e51 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "homebridge-virtual-accessories", - "version": "1.1.0-beta.2", + "version": "1.1.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "homebridge-virtual-accessories", - "version": "1.1.0-beta.2", + "version": "1.1.1", "license": "Apache-2.0", "dependencies": { "@js-joda/core": "^5.6.3", diff --git a/package.json b/package.json index a72ad5a..b40d179 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "homebridge-virtual-accessories", "displayName": "Virtual Accessories for Homebridge", "type": "module", - "version": "1.1.0-beta.2", + "version": "1.1.1", "description": "Virtual accessories for Homebridge.", "author": "justjam2013", "license": "Apache-2.0", diff --git a/src/accessories/virtualAccessoryGarageDoor.ts b/src/accessories/virtualAccessoryGarageDoor.ts index 02f50ab..917cf8e 100644 --- a/src/accessories/virtualAccessoryGarageDoor.ts +++ b/src/accessories/virtualAccessoryGarageDoor.ts @@ -1,3 +1,5 @@ +/* eslint-disable max-len */ + import type { CharacteristicValue, PlatformAccessory } from 'homebridge'; import { VirtualAccessoryPlatform } from '../platform.js'; @@ -14,16 +16,19 @@ export class GarageDoor extends Accessory { static readonly CLOSING: number = 3; // Characteristic.CurrentDoorState.CLOSING; static readonly STOPPED: number = 4; // Characteristic.CurrentDoorState.STOPPED; + private readonly stateStorageKey: string = 'GarageDoorState'; + /** * These are just used to create a working example * You should implement your own code to track the state of your accessory */ private states = { - GarageDoorState: GarageDoor.CLOSED, + GarageDoorCurrentState: GarageDoor.CLOSED, + GarageDoorTargetState: GarageDoor.CLOSED, ObstructionDetected: false, }; - private readonly stateStorageKey: string = 'GarageDoorState'; + private timerId: ReturnType | undefined; constructor( platform: VirtualAccessoryPlatform, @@ -39,13 +44,14 @@ export class GarageDoor extends Accessory { const cachedState = this.loadState(this.storagePath, this.stateStorageKey) as number; if (cachedState !== undefined) { - this.states.GarageDoorState = cachedState; + this.states.GarageDoorCurrentState = cachedState; } else { - this.states.GarageDoorState = this.defaultState; + this.states.GarageDoorCurrentState = this.defaultState; } } else { - this.states.GarageDoorState = this.defaultState; + this.states.GarageDoorCurrentState = this.defaultState; } + this.states.GarageDoorTargetState = this.states.GarageDoorCurrentState; // set accessory information this.accessory.getService(this.platform.Service.AccessoryInformation)! @@ -62,10 +68,10 @@ export class GarageDoor extends Accessory { this.service.setCharacteristic(this.platform.Characteristic.Name, this.accessoryConfiguration.accessoryName); // Update the initial state of the accessory - // eslint-disable-next-line max-len - this.platform.log.debug(`[${this.accessoryConfiguration.accessoryName}] Setting Garage Door Current State: ${this.getStateName(this.states.GarageDoorState)}`); - this.service.updateCharacteristic(this.platform.Characteristic.CurrentDoorState, (this.states.GarageDoorState)); - this.service.updateCharacteristic(this.platform.Characteristic.TargetDoorState, (this.states.GarageDoorState)); + + this.platform.log.debug(`[${this.accessoryConfiguration.accessoryName}] Setting Garage Door Current State: ${this.getStateName(this.states.GarageDoorCurrentState)}`); + this.service.updateCharacteristic(this.platform.Characteristic.CurrentDoorState, (this.states.GarageDoorCurrentState)); + this.service.updateCharacteristic(this.platform.Characteristic.TargetDoorState, (this.states.GarageDoorTargetState)); // each service must implement at-minimum the "required characteristics" for the given service type // see https://developers.homebridge.io/#/service/Lightbulb @@ -101,14 +107,14 @@ export class GarageDoor extends Accessory { */ async handleCurrentDoorStateGet() { // implement your own code to check if the device is on - const garageDoorState = this.states.GarageDoorState; + const garageDoorCurrentState = this.states.GarageDoorCurrentState; - this.platform.log.debug(`[${this.accessoryConfiguration.accessoryName}] Getting Current Garage Door State: ${this.getStateName(garageDoorState)}`); + this.platform.log.debug(`[${this.accessoryConfiguration.accessoryName}] Getting Current Garage Door State: ${this.getStateName(garageDoorCurrentState)}`); // if you need to return an error to show the device as "Not Responding" in the Home app: // throw new this.platform.api.hap.HapStatusError(this.platform.api.hap.HAPStatus.SERVICE_COMMUNICATION_FAILURE); - return garageDoorState; + return garageDoorCurrentState; } /** @@ -117,27 +123,29 @@ export class GarageDoor extends Accessory { */ async handleTargetDoorStateSet(value: CharacteristicValue) { // implement your own code to turn your device on/off - this.states.GarageDoorState = value as number; - - // Store device state if stateful - if (this.accessoryConfiguration.accessoryIsStateful) { - this.saveState(this.storagePath, this.stateStorageKey, this.states.GarageDoorState); - } + this.states.GarageDoorTargetState = value as number; - // eslint-disable-next-line max-len - this.platform.log.info(`[${this.accessoryConfiguration.accessoryName}] Setting Target Garage Door State: ${this.getStateName(this.states.GarageDoorState)}`); + this.platform.log.info(`[${this.accessoryConfiguration.accessoryName}] Setting Target Garage Door State: ${this.getStateName(this.states.GarageDoorTargetState)}`); // CurrentDoorState CLOSING/OPENING - const transition: number = (this.states.GarageDoorState === GarageDoor.OPEN) ? GarageDoor.OPENING : GarageDoor.CLOSING; - this.service!.setCharacteristic(this.platform.Characteristic.CurrentDoorState, (transition)); - this.platform.log.info(`[${this.accessoryConfiguration.accessoryName}] Setting Curent Garage Door State: ${this.getStateName(transition)}`); + const transitionState: number = (this.states.GarageDoorTargetState === GarageDoor.OPEN) ? GarageDoor.OPENING : GarageDoor.CLOSING; + this.service!.setCharacteristic(this.platform.Characteristic.CurrentDoorState, (transitionState)); + this.platform.log.info(`[${this.accessoryConfiguration.accessoryName}] Setting Curent Garage Door State: ${this.getStateName(transitionState)}`); // CurrentDoorState CLOSED/OPEN with 3 second delay const transitionDelayMillis: number = 3 * 1000; - setTimeout(() => { - this.service!.setCharacteristic(this.platform.Characteristic.CurrentDoorState, (this.states.GarageDoorState)); - // eslint-disable-next-line max-len - this.platform.log.info(`[${this.accessoryConfiguration.accessoryName}] Setting Current Garage Door State: ${this.getStateName(this.states.GarageDoorState)}`); + this.timerId = setTimeout(() => { + // Reset timer + clearTimeout(this.timerId); + + this.states.GarageDoorCurrentState = this.states.GarageDoorTargetState; + this.service!.setCharacteristic(this.platform.Characteristic.CurrentDoorState, (this.states.GarageDoorCurrentState)); + this.platform.log.info(`[${this.accessoryConfiguration.accessoryName}] Setting Current Garage Door State: ${this.getStateName(this.states.GarageDoorCurrentState)}`); + + // Store device state if stateful + if (this.accessoryConfiguration.accessoryIsStateful) { + this.saveState(this.storagePath, this.stateStorageKey, this.states.GarageDoorCurrentState); + } }, transitionDelayMillis); } @@ -156,14 +164,14 @@ export class GarageDoor extends Accessory { */ async handleTargetDoorStateGet(): Promise { // implement your own code to check if the device is on - const garageDoorState = this.states.GarageDoorState; + const garageDoorTargetState = this.states.GarageDoorTargetState; - this.platform.log.debug(`[${this.accessoryConfiguration.accessoryName}] Getting Target Garage Door State: ${this.getStateName(garageDoorState)}`); + this.platform.log.debug(`[${this.accessoryConfiguration.accessoryName}] Getting Target Garage Door State: ${this.getStateName(garageDoorTargetState)}`); // if you need to return an error to show the device as "Not Responding" in the Home app: // throw new this.platform.api.hap.HapStatusError(this.platform.api.hap.HAPStatus.SERVICE_COMMUNICATION_FAILURE); - return garageDoorState; + return garageDoorTargetState; } /** diff --git a/src/accessories/virtualAccessoryWindowCovering.ts b/src/accessories/virtualAccessoryWindowCovering.ts index 7453bcc..13667bd 100644 --- a/src/accessories/virtualAccessoryWindowCovering.ts +++ b/src/accessories/virtualAccessoryWindowCovering.ts @@ -17,17 +17,19 @@ export class WindowCovering extends Accessory { static readonly INCREASING: number = 1; // Characteristic.PositionState.INCREASING; -> OPENING static readonly STOPPED: number = 2; // Characteristic.PositionState.STOPPED; -> OPEN or CLOSED + private readonly stateStorageKey: string = 'WindowCoveringPosition'; + /** * These are just used to create a working example * You should implement your own code to track the state of your accessory */ private states = { + WindowCoveringTargetPosition: WindowCovering.CLOSED, WindowCoveringCurrentPosition: WindowCovering.CLOSED, - // WindowCoveringTargetPosition: WindowCovering.CLOSED, WindowCoveringPositionState: WindowCovering.STOPPED, }; - private readonly stateStorageKey: string = 'WindowCoveringPosition'; + private timerId: ReturnType | undefined; constructor( platform: VirtualAccessoryPlatform, @@ -50,6 +52,7 @@ export class WindowCovering extends Accessory { } else { this.states.WindowCoveringCurrentPosition = this.defaultState; } + this.states.WindowCoveringTargetPosition = this.states.WindowCoveringCurrentPosition; // set accessory information this.accessory.getService(this.platform.Service.AccessoryInformation)! @@ -68,7 +71,7 @@ export class WindowCovering extends Accessory { // Update the initial state of the accessory this.platform.log.debug(`[${this.accessoryConfiguration.accessoryName}] Setting Window Covering Current Position: ${this.getStateName(this.states.WindowCoveringCurrentPosition)}`); this.service.updateCharacteristic(this.platform.Characteristic.CurrentPosition, (this.states.WindowCoveringCurrentPosition)); - this.service.updateCharacteristic(this.platform.Characteristic.TargetPosition, (this.states.WindowCoveringCurrentPosition)); + this.service.updateCharacteristic(this.platform.Characteristic.TargetPosition, (this.states.WindowCoveringTargetPosition)); this.service.updateCharacteristic(this.platform.Characteristic.PositionState, (this.states.WindowCoveringPositionState)); // each service must implement at-minimum the "required characteristics" for the given service type @@ -121,14 +124,9 @@ export class WindowCovering extends Accessory { */ async handleTargetPositionSet(value: CharacteristicValue) { // implement your own code to turn your device on/off - this.states.WindowCoveringCurrentPosition = value as number; + this.states.WindowCoveringTargetPosition = value as number; - // Store device state if stateful - if (this.accessoryConfiguration.accessoryIsStateful) { - this.saveState(this.storagePath, this.stateStorageKey, this.states.WindowCoveringCurrentPosition); - } - - this.platform.log.info(`[${this.accessoryConfiguration.accessoryName}] Setting Target Window Covering Position: ${this.getStateName(this.states.WindowCoveringCurrentPosition)}`); + this.platform.log.info(`[${this.accessoryConfiguration.accessoryName}] Setting Target Window Covering Position: ${this.getStateName(this.states.WindowCoveringTargetPosition)}`); // PositionState DECREASING/INCREASING this.states.WindowCoveringPositionState = (this.states.WindowCoveringCurrentPosition === WindowCovering.OPEN) ? WindowCovering.INCREASING : WindowCovering.DECREASING; @@ -138,13 +136,22 @@ export class WindowCovering extends Accessory { // PositionState STOPPED // CurrentPosition OPEN/CLOSED with 3 second delay const transitionDelayMillis: number = 3 * 1000; - setTimeout(() => { + this.timerId = setTimeout(() => { + // Reset timer + clearTimeout(this.timerId); + this.states.WindowCoveringPositionState = WindowCovering.STOPPED; this.service!.setCharacteristic(this.platform.Characteristic.PositionState, (this.states.WindowCoveringPositionState)); this.platform.log.info(`[${this.accessoryConfiguration.accessoryName}] Setting Curent Window Covering State: ${this.getPositionName(this.states.WindowCoveringPositionState)}`); + this.states.WindowCoveringCurrentPosition = this.states.WindowCoveringTargetPosition; this.service!.setCharacteristic(this.platform.Characteristic.CurrentPosition, (this.states.WindowCoveringCurrentPosition)); this.platform.log.info(`[${this.accessoryConfiguration.accessoryName}] Setting Current Garage Door State: ${this.getStateName(this.states.WindowCoveringCurrentPosition)}`); + + // Store device state if stateful + if (this.accessoryConfiguration.accessoryIsStateful) { + this.saveState(this.storagePath, this.stateStorageKey, this.states.WindowCoveringCurrentPosition); + } }, transitionDelayMillis); } @@ -163,14 +170,14 @@ export class WindowCovering extends Accessory { */ async handleTargetPositionGet(): Promise { // implement your own code to check if the device is on - const windowCoveringPosition = this.states.WindowCoveringCurrentPosition; + const windowCoveringTargetPosition = this.states.WindowCoveringTargetPosition; - this.platform.log.debug(`[${this.accessoryConfiguration.accessoryName}] Getting Target Window Covering Position: ${this.getStateName(windowCoveringPosition)}`); + this.platform.log.debug(`[${this.accessoryConfiguration.accessoryName}] Getting Target Window Covering Position: ${this.getStateName(windowCoveringTargetPosition)}`); // if you need to return an error to show the device as "Not Responding" in the Home app: // throw new this.platform.api.hap.HapStatusError(this.platform.api.hap.HAPStatus.SERVICE_COMMUNICATION_FAILURE); - return windowCoveringPosition; + return windowCoveringTargetPosition; } /** @@ -178,14 +185,14 @@ export class WindowCovering extends Accessory { */ async handlePositionStateGet() { // implement your own code to check if the device is on - const windowCoveringPosition = this.states.WindowCoveringPositionState; + const windowCoveringPositionState = this.states.WindowCoveringPositionState; - this.platform.log.debug(`[${this.accessoryConfiguration.accessoryName}] Getting Window Covering State: ${this.getPositionName(windowCoveringPosition)}`); + this.platform.log.debug(`[${this.accessoryConfiguration.accessoryName}] Getting Window Covering State: ${this.getPositionName(windowCoveringPositionState)}`); // if you need to return an error to show the device as "Not Responding" in the Home app: // throw new this.platform.api.hap.HapStatusError(this.platform.api.hap.HAPStatus.SERVICE_COMMUNICATION_FAILURE); - return windowCoveringPosition; + return windowCoveringPositionState; } private getStateName(position: number): string {