Skip to content

Commit

Permalink
Minor optimizations
Browse files Browse the repository at this point in the history
### UPDATED
- Decreased wait time after system arm by 2 seconds to see if Home app's response would be better.
- Another attempt in trying to fix the types for `.onGet`.
- Moved throw errors in `getPanelStatus` and `getSensorStatus` for detecting offline systems to the bottom so that it doesn't affect the other modes (low battery, status fault, etc).
- Logs for "If status has not been retrieved yet" changed from `warn` to `debug`.

### FIXED
- Tamper detection for panel statuses only detected case-sensitive strings.

### ADDED
- "Okay" status for flood sensors.
- Stack tracer in case when the request for arming was not successful.
  • Loading branch information
mrjackyliang committed Jan 1, 2024
1 parent 8d13500 commit ab2532e
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 44 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "homebridge-adt-pulse",
"displayName": "Homebridge ADT Pulse",
"version": "3.0.0-beta.17",
"version": "3.0.0-beta.18",
"description": "Homebridge security system platform for ADT Pulse",
"main": "./build/src/index.js",
"exports": "./build/src/index.js",
Expand Down
84 changes: 44 additions & 40 deletions src/lib/accessory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import chalk from 'chalk';

import { detectedUnknownAccessoryAction } from '@/lib/detect.js';
import { condensedSensorTypeItems } from '@/lib/items.js';
import { condensePanelStates, generateHash } from '@/lib/utility.js';
import { condensePanelStates, generateHash, stackTracer } from '@/lib/utility.js';
import type {
ADTPulseAccessoryAccessory,
ADTPulseAccessoryApi,
Expand Down Expand Up @@ -247,10 +247,10 @@ export class ADTPulseAccessory {
break;
case 'panel':
this.#services.Primary.getCharacteristic(this.#characteristic.SecuritySystemCurrentState)
.onGet(() => this.getPanelStatus('current', context));
.onGet(async () => this.getPanelStatus('current', context));
this.#services.Primary.getCharacteristic(this.#characteristic.SecuritySystemTargetState)
.onGet(() => this.getPanelStatus('target', context))
.onSet((value) => this.setPanelStatus(value, context));
.onGet(async () => this.getPanelStatus('target', context))
.onSet(async (value) => this.setPanelStatus(value, context));
break;
default:
break;
Expand All @@ -260,51 +260,51 @@ export class ADTPulseAccessory {
switch (type) {
case 'co':
this.#services.Primary.getCharacteristic(this.#characteristic.CarbonMonoxideDetected)
.onGet(() => this.getSensorStatus('status', context));
.onGet(async () => this.getSensorStatus('status', context));
break;
case 'doorWindow':
this.#services.Primary.getCharacteristic(this.#characteristic.ContactSensorState)
.onGet(() => this.getSensorStatus('status', context));
.onGet(async () => this.getSensorStatus('status', context));
break;
case 'fire':
this.#services.Primary.getCharacteristic(this.#characteristic.SmokeDetected)
.onGet(() => this.getSensorStatus('status', context));
.onGet(async () => this.getSensorStatus('status', context));
break;
case 'flood':
this.#services.Primary.getCharacteristic(this.#characteristic.LeakDetected)
.onGet(() => this.getSensorStatus('status', context));
.onGet(async () => this.getSensorStatus('status', context));
break;
case 'glass':
this.#services.Primary.getCharacteristic(this.#characteristic.OccupancyDetected)
.onGet(() => this.getSensorStatus('status', context));
.onGet(async () => this.getSensorStatus('status', context));
break;
case 'keypad':
this.#services.Primary.getCharacteristic(this.#characteristic.OccupancyDetected)
.onGet(() => this.getSensorStatus('status', context));
.onGet(async () => this.getSensorStatus('status', context));
break;
case 'motion':
this.#services.Primary.getCharacteristic(this.#characteristic.MotionDetected)
.onGet(() => this.getSensorStatus('status', context));
.onGet(async () => this.getSensorStatus('status', context));
break;
case 'panic':
this.#services.Primary.getCharacteristic(this.#characteristic.OccupancyDetected)
.onGet(() => this.getSensorStatus('status', context));
.onGet(async () => this.getSensorStatus('status', context));
break;
case 'shock':
this.#services.Primary.getCharacteristic(this.#characteristic.OccupancyDetected)
.onGet(() => this.getSensorStatus('status', context));
.onGet(async () => this.getSensorStatus('status', context));
break;
case 'supervisory':
this.#services.Primary.getCharacteristic(this.#characteristic.OccupancyDetected)
.onGet(() => this.getSensorStatus('status', context));
.onGet(async () => this.getSensorStatus('status', context));
break;
case 'temperature':
this.#services.Primary.getCharacteristic(this.#characteristic.CurrentTemperature)
.onGet(() => this.getSensorStatus('status', context));
.onGet(async () => this.getSensorStatus('status', context));
break;
case 'unknown':
this.#services.Primary.getCharacteristic(this.#characteristic.OccupancyDetected)
.onGet(() => this.getSensorStatus('status', context));
.onGet(async () => this.getSensorStatus('status', context));
break;
default:
break;
Expand All @@ -317,13 +317,13 @@ export class ADTPulseAccessory {
break;
case 'panel':
this.#services.Primary.getCharacteristic(this.#characteristic.SecuritySystemAlarmType)
.onGet(() => this.getPanelStatus('alarmType', context));
.onGet(async () => this.getPanelStatus('alarmType', context));

this.#services.Primary.getCharacteristic(this.#characteristic.StatusFault)
.onGet(() => this.getPanelStatus('fault', context));
.onGet(async () => this.getPanelStatus('fault', context));

this.#services.Primary.getCharacteristic(this.#characteristic.StatusTampered)
.onGet(() => this.getPanelStatus('tamper', context));
.onGet(async () => this.getPanelStatus('tamper', context));
break;
default:
break;
Expand All @@ -344,16 +344,16 @@ export class ADTPulseAccessory {
case 'temperature':
case 'unknown':
this.#services.Primary.getCharacteristic(this.#characteristic.StatusActive)
.onGet(() => this.getSensorStatus('active', context));
.onGet(async () => this.getSensorStatus('active', context));

this.#services.Primary.getCharacteristic(this.#characteristic.StatusFault)
.onGet(() => this.getSensorStatus('fault', context));
.onGet(async () => this.getSensorStatus('fault', context));

this.#services.Primary.getCharacteristic(this.#characteristic.StatusLowBattery)
.onGet(() => this.getSensorStatus('lowBattery', context));
.onGet(async () => this.getSensorStatus('lowBattery', context));

this.#services.Primary.getCharacteristic(this.#characteristic.StatusTampered)
.onGet(() => this.getSensorStatus('tamper', context));
.onGet(async () => this.getSensorStatus('tamper', context));
break;
default:
break;
Expand Down Expand Up @@ -398,13 +398,6 @@ export class ADTPulseAccessory {

const { icon, statuses } = matchedSensorStatus;

// If sensor is currently "Offline" or "Unknown".
if (statuses.includes('Offline') || statuses.includes('Unknown')) {
this.#log.warn(`Attempted to get sensor status on ${chalk.underline(name)} (id: ${id}, uuid: ${uuid}) accessory but sensor is currently "Offline" or "Unknown".`);

throw new this.#api.hap.HapStatusError(this.#api.hap.HAPStatus.NOT_ALLOWED_IN_CURRENT_STATE);
}

// Find the state for "Sensor" (required characteristic).
if (mode === 'status') {
switch (type) {
Expand All @@ -428,7 +421,9 @@ export class ADTPulseAccessory {
}
break;
case 'flood':
// TODO: Nothing done here yet.
if (statuses.includes('Okay')) {
return this.#characteristic.LeakDetected.LEAK_NOT_DETECTED;
}
break;
case 'glass':
if (statuses.includes('Tripped')) {
Expand Down Expand Up @@ -517,6 +512,13 @@ export class ADTPulseAccessory {
return this.#characteristic.StatusTampered.NOT_TAMPERED;
}

// If sensor is currently "Offline" or "Unknown". Should be detected last to prevent breaking other modes.
if (statuses.includes('Offline') || statuses.includes('Unknown')) {
this.#log.warn(`Attempted to get sensor status on ${chalk.underline(name)} (id: ${id}, uuid: ${uuid}) accessory but sensor is currently "Offline" or "Unknown".`);

throw new this.#api.hap.HapStatusError(this.#api.hap.HAPStatus.NOT_ALLOWED_IN_CURRENT_STATE);
}

this.#log.warn(`Attempted to get sensor status on ${chalk.underline(name)} (id: ${id}, uuid: ${uuid}) accessory but actions have not been implemented yet.`);

await this.newInformationDispatcher(type, matchedSensorStatus, context);
Expand Down Expand Up @@ -557,20 +559,13 @@ export class ADTPulseAccessory {
|| this.#state.data.panelStatus.panelStates.length === 0
|| this.#state.data.panelStatus.panelStatuses.length === 0
) {
this.#log.warn(`Attempted to get panel status on ${chalk.underline(name)} (id: ${id}, uuid: ${uuid}) accessory but panel status has not been retrieved yet.`);
this.#log.debug(`Attempted to get panel status on ${chalk.underline(name)} (id: ${id}, uuid: ${uuid}) accessory but panel status has not been retrieved yet.`);

throw new this.#api.hap.HapStatusError(this.#api.hap.HAPStatus.RESOURCE_BUSY);
}

const { panelStates, panelStatuses } = this.#state.data.panelStatus;

// If panel state is "Status Unavailable".
if (panelStates.includes('Status Unavailable')) {
this.#log.warn(`Attempted to get panel status on ${chalk.underline(name)} (id: ${id}, uuid: ${uuid}) accessory but panel state is "Status Unavailable".`);

throw new this.#api.hap.HapStatusError(this.#api.hap.HAPStatus.RESOURCE_BUSY);
}

// If mode is "alarmType".
if (mode === 'alarmType') {
if (
Expand Down Expand Up @@ -621,7 +616,7 @@ export class ADTPulseAccessory {
// If mode is "tamper".
if (mode === 'tamper') {
// TODO: Not enough statuses currently to determine whether system is tampered or not.
if (!panelStatuses.some((panelStatus) => panelStatus.includes('tamper'))) {
if (!panelStatuses.some((panelStatus) => /tamp/gi.test(panelStatus))) {
return this.#characteristic.StatusTampered.NOT_TAMPERED;
}
}
Expand All @@ -642,6 +637,13 @@ export class ADTPulseAccessory {
}
}

// If panel state is "Status Unavailable". Should be detected last to prevent breaking other modes.
if (panelStates.includes('Status Unavailable')) {
this.#log.warn(`Attempted to get panel status on ${chalk.underline(name)} (id: ${id}, uuid: ${uuid}) accessory but panel state is "Status Unavailable".`);

throw new this.#api.hap.HapStatusError(this.#api.hap.HAPStatus.RESOURCE_BUSY);
}

this.#log.warn(`Attempted to get panel status on ${chalk.underline(name)} (id: ${id}, uuid: ${uuid}) accessory but actions have not been implemented yet.`);

await this.newInformationDispatcher(type, this.#state.data.panelStatus, context);
Expand Down Expand Up @@ -729,6 +731,8 @@ export class ADTPulseAccessory {
if (!result.success) {
this.#log.error(`Attempted to set panel status on ${chalk.underline(name)} (id: ${id}, uuid: ${uuid}) accessory but request was not successful.`);

stackTracer('api-response', result);

throw new this.#api.hap.HapStatusError(this.#api.hap.HAPStatus.OPERATION_TIMED_OUT);
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/lib/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2702,8 +2702,8 @@ export class ADTPulse {
// After changing any arm state, the "armState" may be different from when you logged into the portal.
this.#session.isCleanState = false;

// Allow the security orb buttons to refresh (usually takes around 6 seconds).
await sleep(6000);
// Allow the security orb buttons to refresh (usually takes around 4 seconds).
await sleep(4000);

// sessions.axiosSummary: Load the summary page.
sessions.axiosSummary = await this.#session.httpClient.get<unknown>(
Expand Down
2 changes: 1 addition & 1 deletion src/types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -561,7 +561,7 @@ export type ADTPulseAccessoryGetPanelStatusMode = 'alarmType' | 'current' | 'fau

export type ADTPulseAccessoryGetPanelStatusContext = Device;

export type ADTPulseAccessoryGetPanelStatusReturns = Promise<CharacteristicValue>;
export type ADTPulseAccessoryGetPanelStatusReturns = Promise<Nullable<CharacteristicValue>>;

/**
* ADT Pulse Accessory - New information dispatcher.
Expand Down

0 comments on commit ab2532e

Please sign in to comment.