From 9872e58c42d4e620ad5bb549384ed8920d6862ee Mon Sep 17 00:00:00 2001 From: Cameron Little Date: Sat, 20 Jan 2024 13:48:05 -0700 Subject: [PATCH] Delay marking unoccupied Resolves #2 --- config.schema.json | 8 ++++++ src/config.ts | 1 + src/platform.ts | 60 ++++++++++++++++++++++++++++++---------- src/platformAccessory.ts | 2 +- 4 files changed, 56 insertions(+), 15 deletions(-) diff --git a/config.schema.json b/config.schema.json index 6f4874e..e3b2c0a 100644 --- a/config.schema.json +++ b/config.schema.json @@ -54,6 +54,14 @@ "default": 5000, "required": true }, + "occupancyTimeout": { + "title": "Occupancy Timeout", + "type": "number", + "description": "Time in milliseconds to wait before setting unoccupied (approximately). See https://github.com/apexskier/homebridge-eero-presence/issues/2 for more info.", + "minimum": 1, + "default": 20000, + "required": true + }, "enableStatusLightAccessories": { "title": "Enable Status Light Accessories", "type": "boolean", diff --git a/src/config.ts b/src/config.ts index ab50fa3..48cbeb0 100644 --- a/src/config.ts +++ b/src/config.ts @@ -1,5 +1,6 @@ export interface Config { userToken: string; + occupancyTimeout?: number; pollTime?: number; network?: string; minSignal?: number; diff --git a/src/platform.ts b/src/platform.ts index a534712..c21c15c 100644 --- a/src/platform.ts +++ b/src/platform.ts @@ -147,8 +147,10 @@ export class EeroPresenceHomebridgePlatform implements DynamicPlatformPlugin { }); } + private lastOccupied = new Map(); + private async checkOccupancy(deviceEndpoint: string) { - const { data: devices } = await ( + const { data: devices } = (await ( await fetch(`https://api-user.e2ro.com${deviceEndpoint}`, { method: "GET", headers: { @@ -156,7 +158,18 @@ export class EeroPresenceHomebridgePlatform implements DynamicPlatformPlugin { cookie: `s=${this.config.userToken}`, }, }) - ).json(); + ).json()) as { + data: ReadonlyArray<{ + connected: boolean; + connection_type: string; + connectivity: { score: number }; + device_type: string; + display_name: string; + source: { serial_number: string; location: string }; + }>; + }; + + const now = new Date(); const connectedDevices = devices .filter( @@ -170,11 +183,9 @@ export class EeroPresenceHomebridgePlatform implements DynamicPlatformPlugin { .filter( ({ connectivity: { score } }) => score > (this.config.minSignal || 0.7), ); - const connectedEeros = new Set( - connectedDevices.map(({ source: { serial_number } }) => - this.api.hap.uuid.generate(serial_number), - ), - ); + connectedDevices.forEach(({ source: { serial_number } }) => { + this.lastOccupied.set(this.api.hap.uuid.generate(serial_number), now); + }); this.log.debug( "connected devices:", connectedDevices @@ -184,15 +195,36 @@ export class EeroPresenceHomebridgePlatform implements DynamicPlatformPlugin { ) .join(", "), ); + this.accessories.forEach((accessory) => { - accessory - ?.getService(this.Service.OccupancySensor) - ?.updateCharacteristic( + const status = + now.getTime() - + (this.lastOccupied.get(accessory.UUID)?.getTime() ?? 0) < + (this.config.occupancyTimeout ?? 20000) + ? this.Characteristic.OccupancyDetected.OCCUPANCY_DETECTED + : this.Characteristic.OccupancyDetected.OCCUPANCY_NOT_DETECTED; + const occupancyService = accessory.getService( + this.Service.OccupancySensor, + ); + if (occupancyService) { + const detected = occupancyService.getCharacteristic( this.Characteristic.OccupancyDetected, - connectedEeros.has(accessory.UUID) - ? this.Characteristic.OccupancyDetected.OCCUPANCY_DETECTED - : this.Characteristic.OccupancyDetected.OCCUPANCY_NOT_DETECTED, - ); + ).value; + if (detected !== status) { + this.log.info( + `${accessory.displayName} now ${ + status === + this.Characteristic.OccupancyDetected.OCCUPANCY_DETECTED + ? "occupied" + : "unoccupied" + }`, + ); + occupancyService?.updateCharacteristic( + this.Characteristic.OccupancyDetected, + status, + ); + } + } }); } diff --git a/src/platformAccessory.ts b/src/platformAccessory.ts index c3929ca..81eb1c6 100644 --- a/src/platformAccessory.ts +++ b/src/platformAccessory.ts @@ -124,7 +124,7 @@ export class EeroPresencePlatformAccessory { } async fetch(input: RequestInfo | URL, init?: RequestInit) { - let response; + let response: Response; try { response = await fetch(input, { ...init,