Skip to content

Commit

Permalink
feat: Update garage door status pooling logic
Browse files Browse the repository at this point in the history
  • Loading branch information
KieraDOG committed Aug 1, 2024
1 parent 9ec4254 commit c1a2fc1
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 34 deletions.
65 changes: 49 additions & 16 deletions src/CGDGarageDoor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,20 @@ enum DoorState {
Error,
}

type StatusUpdateListener = () => void;


export class CGDGarageDoor {
private readonly log: Logging;
private config: Config;
private status?: Status;
private statusUpdateListener?: StatusUpdateListener;

constructor(log: Logging, config: Config) {
this.log = log;
this.config = config;

this.poolStatus();
}

private run = async ({ cmd, value, softValue = value }) => {
Expand Down Expand Up @@ -63,9 +69,12 @@ export class CGDGarageDoor {
}, {
retries: 3,
onRetry: (error, retries) => {
this.log.warn(`Failed to run command[Retrying ${retries}]: ${cmd}=${value}`);
this.log.warn(`Failed to run command [${retries} retries]: ${cmd}=${value}`);
this.log.warn(JSON.stringify(error));
},
onRecover: (retries) => {
this.log.info(`Recovered to run command [${retries} retries]: ${cmd}=${value}`);
},
onFail: (error) => {
this.log.error(`Failed to run command: ${cmd}=${value}`);
this.log.error(JSON.stringify(error));
Expand All @@ -87,6 +96,13 @@ export class CGDGarageDoor {
this.status = data as Status;
};

private poolStatus = async () => {
await this.refreshStatus();
this.statusUpdateListener?.();

setTimeout(this.poolStatus, 2000);
};

private getDoorState = (): DoorState => {
if (this.status?.door.startsWith('Closed')) {
return DoorState.Closed;
Expand All @@ -108,25 +124,38 @@ export class CGDGarageDoor {
return DoorState.Stopped;
}

this.log.error(`[getDoorCurrentState] Unknown door status: ${this.status?.door}`);
this.log.error(`[getDoorState] Unknown door status: ${this.status?.door}`);

return DoorState.Error;
};

public poolStatus = async () => {
await this.refreshStatus();

setTimeout(this.poolStatus, 2000);
public onStatusUpdate = (listener: StatusUpdateListener) => {
this.statusUpdateListener = listener;
};

public getDoorCurrentState = (): number => {
public waitForStatus = () => new Promise<void>((resolve) => {
this.log.info('Pulse - Ping...');
const interval = setInterval(() => {
if (this.status) {
this.log.info('Pulse - Pong!');
clearInterval(interval);
resolve();
}
}, 1000);
});

public getCurrentDoorState = (): number => {
const doorState = this.getDoorState();

// static readonly OPEN = 0;
// static readonly CLOSED = 1;
// static readonly OPENING = 2;
// static readonly CLOSING = 3;
// static readonly STOPPED = 4;
// export declare class CurrentDoorState extends Characteristic {
// static readonly UUID: string;
// static readonly OPEN = 0;
// static readonly CLOSED = 1;
// static readonly OPENING = 2;
// static readonly CLOSING = 3;
// static readonly STOPPED = 4;
// constructor();
// }

if (doorState === DoorState.Error) {
this.log.error(`[getDoorCurrentState] Unknown door state: ${doorState}`);
Expand All @@ -142,11 +171,15 @@ export class CGDGarageDoor {
}[doorState];
};

public getDoorTargetState = (): number => {
public getTargetDoorState = (): number => {
const doorState = this.getDoorState();

// static readonly OPEN = 0;
// static readonly CLOSED = 1;
// export declare class TargetDoorState extends Characteristic {
// static readonly UUID: string;
// static readonly OPEN = 0;
// static readonly CLOSED = 1;
// constructor();
// }

if (doorState === DoorState.Error) {
this.log.error(`[getDoorTargetState] Unknown door state: ${doorState}`);
Expand All @@ -162,7 +195,7 @@ export class CGDGarageDoor {
}[doorState];
};

public setDoorTargetState = async (value: number): Promise<void> => {
public setTargetDoorState = async (value: number): Promise<void> => {
if (value === 0) {
this.log.debug('Opening door...');
await this.run({ cmd: 'door', value: 'open', softValue: 'Opening' });
Expand Down
26 changes: 10 additions & 16 deletions src/platform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@ export class CGDCameraPlatform implements DynamicPlatformPlugin {

private readonly accessories: PlatformAccessory[] = [];

private refreshInterval?: NodeJS.Timer;

constructor(log: Logging, config: PlatformConfig, api: API) {
this.log = log;
this.api = api;
Expand All @@ -39,13 +37,6 @@ export class CGDCameraPlatform implements DynamicPlatformPlugin {
this.log('Did finish launching');
this.addAccessory(deviceHostname, cgdGarageDoor);
});

api.on(APIEvent.SHUTDOWN, () => {
this.log('SHUTDOWN');
if (this.refreshInterval) {
clearInterval(this.refreshInterval);
}
});
}

configureAccessory(accessory: PlatformAccessory): void {
Expand All @@ -54,7 +45,7 @@ export class CGDCameraPlatform implements DynamicPlatformPlugin {
}

async addAccessory(name: string, cgdGarageDoor: CGDGarageDoor) {
await cgdGarageDoor.poolStatus();
await cgdGarageDoor.waitForStatus();

this.log('Adding new accessory with name %s', name);

Expand Down Expand Up @@ -86,11 +77,11 @@ export class CGDCameraPlatform implements DynamicPlatformPlugin {
const garageDoorOpener = accessory.getService(this.api.hap.Service.GarageDoorOpener) || accessory.addService(new this.api.hap.Service.GarageDoorOpener(accessory.displayName));

garageDoorOpener.getCharacteristic(this.api.hap.Characteristic.CurrentDoorState)
.onGet(() => cgdGarageDoor.getDoorCurrentState());
.onGet(() => cgdGarageDoor.getCurrentDoorState());

garageDoorOpener.getCharacteristic(this.api.hap.Characteristic.TargetDoorState)
.onGet(() => cgdGarageDoor.getDoorTargetState())
.onSet((value) => cgdGarageDoor.setDoorTargetState(+value));
.onGet(() => cgdGarageDoor.getTargetDoorState())
.onSet((value) => cgdGarageDoor.setTargetDoorState(+value));

const lightbulb = accessory.getService(this.api.hap.Service.Lightbulb) || accessory.addService(new this.api.hap.Service.Lightbulb(accessory.displayName));

Expand All @@ -104,16 +95,19 @@ export class CGDCameraPlatform implements DynamicPlatformPlugin {
.onGet(() => cgdGarageDoor.getVacation())
.onSet((value) => cgdGarageDoor.setVacation(value));

this.refreshInterval = setInterval(() => {
cgdGarageDoor.onStatusUpdate(() => {
garageDoorOpener
.getCharacteristic(this.api.hap.Characteristic.CurrentDoorState).updateValue(cgdGarageDoor.getDoorCurrentState());
.getCharacteristic(this.api.hap.Characteristic.CurrentDoorState).updateValue(cgdGarageDoor.getCurrentDoorState());

garageDoorOpener
.getCharacteristic(this.api.hap.Characteristic.TargetDoorState).updateValue(cgdGarageDoor.getTargetDoorState());

lightbulb
.getCharacteristic(this.api.hap.Characteristic.On).updateValue(cgdGarageDoor.getLightbulb());

vacationSwitch
.getCharacteristic(this.api.hap.Characteristic.On).updateValue(cgdGarageDoor.getVacation());
}, 2000);
});

this.log('Garage Door Accessory %s configured!', accessory.displayName);
}
Expand Down
13 changes: 11 additions & 2 deletions src/retry.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,22 @@
interface Config {
retries: number;
isRetry?: boolean;
onRetry: (error: unknown, retries: number) => void;
onRecover: (retries: number) => void;
onFail: (error: unknown) => void;
}

const retry = async (fn: () => Promise<unknown>, config: Config) => {
const { retries, onRetry, onFail } = config;
const { retries, onRetry, onRecover, onFail, isRetry } = config;

try {
return await fn();
const data = await fn();

if (isRetry) {
onRecover(retries);
}

return data;
} catch (error) {
if (retries === 0) {
return onFail(error);
Expand All @@ -18,6 +26,7 @@ const retry = async (fn: () => Promise<unknown>, config: Config) => {

return retry(fn, {
...config,
isRetry: true,
retries: retries - 1,
});
}
Expand Down

0 comments on commit c1a2fc1

Please sign in to comment.