Skip to content

Commit

Permalink
Moderate changes and fixes
Browse files Browse the repository at this point in the history
### UPDATED
- Syncing the `.editorconfig` file.
- `parseSensorsTable()` will now skip devices that have a "System/Supervisory" device type.
- Renamed the detector functions to match where should they be called from (Api or Platform).
- Detector reporting url.
- Re-wrote the detector reminder and moved it to after the content.

### FIXED
- Incorrect detection of "Status Tampered" in connection with "Trouble" status.
- Headers for `test-api.ts` and `repl.ts` files.
- `generateFakeReadyButtons()` should not be adding an Arm Night button because this is not available in the portal.
- Type for `StackTracerError` was incorrectly defined.

### ADDED
- Sensor status support for "Tampered".
- Function name type for `detectApiDebugParser()`.

### REMOVED
- Support for "System/Supervisory" because it does not show a status in the summary page.
  • Loading branch information
mrjackyliang committed Jan 18, 2024
1 parent 2fb1cd5 commit 5b6d5bb
Show file tree
Hide file tree
Showing 16 changed files with 620 additions and 304 deletions.
3 changes: 3 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ ij_html_text_wrap = off
[*.php]
indent_size = 4
indent_style = tab
ij_php_indent_code_in_php_tags = true
ij_php_method_brace_style = end_of_line
ij_php_new_line_after_php_opening_tag = true

[{*.markdown,*.md}]
ij_markdown_max_lines_around_block_elements = 0
Expand Down
3 changes: 1 addition & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,7 @@ This plugin can expose these devices (in read-only mode) based on your configura
6. `heat` - Heat (Rate-of-Rise) Detector
7. `motion` - Motion Sensor __::__ Motion Sensor (Notable Events Only)
8. `shock` - Shock Sensor
9. `supervisory` - System/Supervisory
10. `temperature` - Temperature Sensor
9. `temperature` - Temperature Sensor

Due to implementation complexity and platform instability, all Z-Wave accessories connected to the ADT Pulse gateway will not be planned for development or be supported overall. Consider purchasing the [Hubitat Hub](https://hubitat.com) for a seamless setup experience, or read about the [Home Assistant Z-Wave](https://www.home-assistant.io/integrations/zwave_js/) integration.

Expand Down
6 changes: 0 additions & 6 deletions config.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -205,12 +205,6 @@
"shock"
]
},
{
"title": "System/Supervisory",
"enum": [
"supervisory"
]
},
{
"title": "Temperature Sensor",
"enum": [
Expand Down
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",
"version": "3.0.1",
"description": "Homebridge security system platform for ADT Pulse",
"main": "./build/src/index.js",
"exports": "./build/src/index.js",
Expand Down
14 changes: 2 additions & 12 deletions src/lib/accessory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -208,9 +208,6 @@ export class ADTPulseAccessory {
case 'shock':
this.#services.Primary = this.#accessory.getService(service.OccupancySensor) ?? this.#accessory.addService(service.OccupancySensor);
break;
case 'supervisory':
this.#services.Primary = this.#accessory.getService(service.OccupancySensor) ?? this.#accessory.addService(service.OccupancySensor);
break;
case 'temperature':
this.#services.Primary = this.#accessory.getService(service.TemperatureSensor) ?? this.#accessory.addService(service.TemperatureSensor);
break;
Expand Down Expand Up @@ -327,10 +324,6 @@ export class ADTPulseAccessory {
this.#services.Primary.getCharacteristic(this.#characteristic.OccupancyDetected)
.updateValue(this.getSensorStatus('status'));
break;
case 'supervisory':
this.#services.Primary.getCharacteristic(this.#characteristic.OccupancyDetected)
.updateValue(this.getSensorStatus('status'));
break;
case 'temperature':
this.#services.Primary.getCharacteristic(this.#characteristic.CurrentTemperature)
.updateValue(this.getSensorStatus('status'));
Expand All @@ -349,7 +342,6 @@ export class ADTPulseAccessory {
case 'heat':
case 'motion':
case 'shock':
case 'supervisory':
case 'temperature':
this.#services.Primary.getCharacteristic(this.#characteristic.StatusActive)
.updateValue(this.getSensorStatus('active'));
Expand Down Expand Up @@ -428,6 +420,7 @@ export class ADTPulseAccessory {
if (
statuses.includes('ALARM')
|| statuses.includes('Bypassed')
|| statuses.includes('Trouble')
|| icon === 'devStatAlarm'
) {
return this.#characteristic.StatusFault.GENERAL_FAULT;
Expand All @@ -453,7 +446,7 @@ export class ADTPulseAccessory {
if (mode === 'tamper') {
// If status or icon includes these, the sensor is tampered.
if (
statuses.includes('Trouble')
statuses.includes('Tampered')
|| icon === 'devStatTamper'
) {
return this.#characteristic.StatusTampered.TAMPERED;
Expand Down Expand Up @@ -533,9 +526,6 @@ export class ADTPulseAccessory {
case 'shock':
break;
// TODO Device type needs to be manually tested and confirmed first.
case 'supervisory':
break;
// TODO Device type needs to be manually tested and confirmed first.
case 'temperature':
/**
* Since sensors from ADT do not show exact temperatures
Expand Down
150 changes: 133 additions & 17 deletions src/lib/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@ import { serializeError } from 'serialize-error';
import { CookieJar } from 'tough-cookie';

import {
detectedNewDoSubmitHandlers,
detectedNewGatewayInformation,
detectedNewOrbSecurityButtons,
detectedNewPanelInformation,
detectedNewPanelStatus,
detectedNewPortalVersion,
detectedNewSensorsInformation,
detectedNewSensorsStatus,
detectApiDebugParser,
detectApiDoSubmitHandlers,
detectApiGatewayInformation,
detectApiOrbSecurityButtons,
detectApiPanelInformation,
detectApiPanelStatus,
detectApiPortalVersion,
detectApiSensorsInformation,
detectApiSensorsStatus,
} from '@/lib/detect.js';
import {
paramNetworkId,
Expand Down Expand Up @@ -41,6 +42,7 @@ import {
generateDynatracePCHeaderValue,
generateFakeReadyButtons,
generateHash,
isEmptyOrbTextSummary,
isPortalSyncCode,
parseArmDisarmMessage,
parseDoSubmitHandlers,
Expand Down Expand Up @@ -864,6 +866,20 @@ export class ADTPulse {
*/
await this.newInformationDispatcher('gateway-information', gatewayInformation);

// If the parsing function may be parsing data incorrectly.
if (Object.keys(fetchedTableCells).length !== 18) {
if (this.#internal.debug) {
debugLog(this.#internal.logger, 'api.ts / ADTPulse.getGatewayInformation()', 'warn', 'The fetchTableCells() function may be parsing the gateway information incorrectly');
}

await this.newInformationDispatcher('debug-parser', {
parserName: 'fetchTableCells()',
parserReason: 'length does not equal to 18',
parserResponse: fetchedTableCells,
rawData: sessions.axiosSystemGateway.data,
});
}

if (this.#internal.debug) {
debugLog(this.#internal.logger, 'api.ts / ADTPulse.getGatewayInformation()', 'success', `Successfully retrieved gateway information from "${this.#internal.baseUrl}"`);
}
Expand Down Expand Up @@ -1062,6 +1078,20 @@ export class ADTPulse {
*/
await this.newInformationDispatcher('panel-information', panelInformation);

// If the parsing function may be parsing data incorrectly.
if (Object.keys(fetchedTableCells).length !== 5) {
if (this.#internal.debug) {
debugLog(this.#internal.logger, 'api.ts / ADTPulse.getPanelInformation()', 'warn', 'The fetchTableCells() function may be parsing the panel information incorrectly');
}

await this.newInformationDispatcher('debug-parser', {
parserName: 'fetchTableCells()',
parserReason: 'length does not equal to 5',
parserResponse: fetchedTableCells,
rawData: sessions.axiosSystemDeviceId1.data,
});
}

if (this.#internal.debug) {
debugLog(this.#internal.logger, 'api.ts / ADTPulse.getPanelInformation()', 'success', `Successfully retrieved panel information from "${this.#internal.baseUrl}"`);
}
Expand Down Expand Up @@ -1277,6 +1307,20 @@ export class ADTPulse {
*/
await this.newInformationDispatcher('panel-status', parsedOrbTextSummary);

// If the parsing function may be parsing data incorrectly.
if (isEmptyOrbTextSummary(parsedOrbTextSummary)) {
if (this.#internal.debug) {
debugLog(this.#internal.logger, 'api.ts / ADTPulse.getPanelStatus()', 'warn', 'The parseOrbTextSummary() function may be parsing sensors information incorrectly');
}

await this.newInformationDispatcher('debug-parser', {
parserName: 'parseOrbTextSummary()',
parserReason: 'empty response',
parserResponse: parsedOrbTextSummary,
rawData: sessions.axiosSummary.data,
});
}

if (this.#internal.debug) {
debugLog(this.#internal.logger, 'api.ts / ADTPulse.getPanelStatus()', 'success', `Successfully retrieved panel status from "${this.#internal.baseUrl}"`);
}
Expand Down Expand Up @@ -1593,6 +1637,20 @@ export class ADTPulse {
*/
await this.newInformationDispatcher('orb-security-buttons', parsedOrbSecurityButtons);

// If the parsing function may be parsing data incorrectly.
if (parsedOrbSecurityButtons.length < 1 || parsedOrbSecurityButtons.length > 3) {
if (this.#internal.debug) {
debugLog(this.#internal.logger, 'api.ts / ADTPulse.setPanelStatus()', 'warn', 'The parseOrbSecurityButtons() function may be parsing the orb security buttons incorrectly');
}

await this.newInformationDispatcher('debug-parser', {
parserName: 'parseOrbSecurityButtons()',
parserReason: 'length is not between 1 to 3',
parserResponse: parsedOrbSecurityButtons,
rawData: sessions.axiosSummary.data,
});
}

// Only keep all ready (enabled) orb security buttons.
let readyButtons = parsedOrbSecurityButtons.filter((parsedOrbSecurityButton): parsedOrbSecurityButton is ADTPulseSetPanelStatusReadyButton => !parsedOrbSecurityButton.buttonDisabled);

Expand Down Expand Up @@ -1923,7 +1981,6 @@ export class ADTPulse {
* 'Motion Sensor'
* 'Motion Sensor (Notable Events Only)'
* 'Shock Sensor'
* 'System/Supervisory'
* 'Temperature Sensor'
* 'Water/Flood Sensor'
* 'Window Sensor'
Expand All @@ -1937,6 +1994,20 @@ export class ADTPulse {
*/
await this.newInformationDispatcher('sensors-information', parsedSensorsTable);

// If the parsing function may be parsing data incorrectly.
if (parsedSensorsTable.length < 1) {
if (this.#internal.debug) {
debugLog(this.#internal.logger, 'api.ts / ADTPulse.getSensorsInformation()', 'warn', 'The parseSensorsTable() function may be parsing the sensors table incorrectly');
}

await this.newInformationDispatcher('debug-parser', {
parserName: 'parseSensorsTable()',
parserReason: 'length is not 1 or more',
parserResponse: parsedSensorsTable,
rawData: sessions.axiosSystem.data,
});
}

if (this.#internal.debug) {
debugLog(this.#internal.logger, 'api.ts / ADTPulse.getSensorsInformation()', 'success', `Successfully retrieved sensors information from "${this.#internal.baseUrl}"`);
}
Expand Down Expand Up @@ -2175,6 +2246,20 @@ export class ADTPulse {
*/
await this.newInformationDispatcher('sensors-status', parsedOrbSensors);

// If the parsing function may be parsing data incorrectly.
if (parsedOrbSensors.length < 1) {
if (this.#internal.debug) {
debugLog(this.#internal.logger, 'api.ts / ADTPulse.getSensorsStatus()', 'warn', 'The parseOrbSensors() function may be parsing the orb sensors incorrectly');
}

await this.newInformationDispatcher('debug-parser', {
parserName: 'parseOrbSensors()',
parserReason: 'length is not 1 or more',
parserResponse: parsedOrbSensors,
rawData: sessions.axiosSummary.data,
});
}

if (this.#internal.debug) {
debugLog(this.#internal.logger, 'api.ts / ADTPulse.getSensorsStatus()', 'success', `Successfully retrieved sensors status from "${this.#internal.baseUrl}"`);
}
Expand Down Expand Up @@ -2888,6 +2973,20 @@ export class ADTPulse {
*/
await this.newInformationDispatcher('orb-security-buttons', parsedOrbSecurityButtons);

// If the parsing function may be parsing data incorrectly.
if (parsedOrbSecurityButtons.length < 1 || parsedOrbSecurityButtons.length > 3) {
if (this.#internal.debug) {
debugLog(this.#internal.logger, 'api.ts / ADTPulse.armDisarmHandler()', 'warn', 'The parseOrbSecurityButtons() function may be parsing the orb security buttons incorrectly');
}

await this.newInformationDispatcher('debug-parser', {
parserName: 'parseOrbSecurityButtons()',
parserReason: 'length is not between 1 to 3',
parserResponse: parsedOrbSecurityButtons,
rawData: sessions.axiosSummary.data,
});
}

let readyButtons = parsedOrbSecurityButtons.filter((parsedOrbSecurityButton): parsedOrbSecurityButton is ADTPulseArmDisarmHandlerReadyButton => !parsedOrbSecurityButton.buttonDisabled);

// Generate "fake" ready buttons if arming tasks become stuck.
Expand Down Expand Up @@ -3050,6 +3149,20 @@ export class ADTPulse {
*/
await this.newInformationDispatcher('do-submit-handlers', parsedDoSubmitHandlers);

// If the parsing function may be parsing data incorrectly.
if (parsedDoSubmitHandlers.length !== 2) {
if (this.#internal.debug) {
debugLog(this.#internal.logger, 'api.ts / ADTPulse.armDisarmHandler()', 'warn', 'The parseDoSubmitHandlers() function may be parsing the do submit handlers incorrectly');
}

await this.newInformationDispatcher('debug-parser', {
parserName: 'parseDoSubmitHandlers()',
parserReason: 'length does not equal to 2',
parserResponse: parsedDoSubmitHandlers,
rawData: response.data,
});
}

// Check if there are no force arm buttons available.
if (
parsedDoSubmitHandlers.length === 0
Expand Down Expand Up @@ -3314,29 +3427,32 @@ export class ADTPulse {

// Determine what information needs to be checked.
switch (type) {
case 'debug-parser':
detectedNew = await detectApiDebugParser(data as ADTPulseNewInformationDispatcherData<'debug-parser'>, this.#internal.logger, this.#internal.debug);
break;
case 'do-submit-handlers':
detectedNew = await detectedNewDoSubmitHandlers(data as ADTPulseNewInformationDispatcherData<'do-submit-handlers'>, this.#internal.logger, this.#internal.debug);
detectedNew = await detectApiDoSubmitHandlers(data as ADTPulseNewInformationDispatcherData<'do-submit-handlers'>, this.#internal.logger, this.#internal.debug);
break;
case 'gateway-information':
detectedNew = await detectedNewGatewayInformation(data as ADTPulseNewInformationDispatcherData<'gateway-information'>, this.#internal.logger, this.#internal.debug);
detectedNew = await detectApiGatewayInformation(data as ADTPulseNewInformationDispatcherData<'gateway-information'>, this.#internal.logger, this.#internal.debug);
break;
case 'orb-security-buttons':
detectedNew = await detectedNewOrbSecurityButtons(data as ADTPulseNewInformationDispatcherData<'orb-security-buttons'>, this.#internal.logger, this.#internal.debug);
detectedNew = await detectApiOrbSecurityButtons(data as ADTPulseNewInformationDispatcherData<'orb-security-buttons'>, this.#internal.logger, this.#internal.debug);
break;
case 'panel-information':
detectedNew = await detectedNewPanelInformation(data as ADTPulseNewInformationDispatcherData<'panel-information'>, this.#internal.logger, this.#internal.debug);
detectedNew = await detectApiPanelInformation(data as ADTPulseNewInformationDispatcherData<'panel-information'>, this.#internal.logger, this.#internal.debug);
break;
case 'panel-status':
detectedNew = await detectedNewPanelStatus(data as ADTPulseNewInformationDispatcherData<'panel-status'>, this.#internal.logger, this.#internal.debug);
detectedNew = await detectApiPanelStatus(data as ADTPulseNewInformationDispatcherData<'panel-status'>, this.#internal.logger, this.#internal.debug);
break;
case 'portal-version':
detectedNew = await detectedNewPortalVersion(data as ADTPulseNewInformationDispatcherData<'portal-version'>, this.#internal.logger, this.#internal.debug);
detectedNew = await detectApiPortalVersion(data as ADTPulseNewInformationDispatcherData<'portal-version'>, this.#internal.logger, this.#internal.debug);
break;
case 'sensors-information':
detectedNew = await detectedNewSensorsInformation(data as ADTPulseNewInformationDispatcherData<'sensors-information'>, this.#internal.logger, this.#internal.debug);
detectedNew = await detectApiSensorsInformation(data as ADTPulseNewInformationDispatcherData<'sensors-information'>, this.#internal.logger, this.#internal.debug);
break;
case 'sensors-status':
detectedNew = await detectedNewSensorsStatus(data as ADTPulseNewInformationDispatcherData<'sensors-status'>, this.#internal.logger, this.#internal.debug);
detectedNew = await detectApiSensorsStatus(data as ADTPulseNewInformationDispatcherData<'sensors-status'>, this.#internal.logger, this.#internal.debug);
break;
default:
break;
Expand Down
Loading

0 comments on commit 5b6d5bb

Please sign in to comment.