homebridge-adt-pulse 3.0.0-beta.9 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +198 -12
- package/README.md +168 -106
- package/build/config.schema.json +18 -13
- package/build/src/lib/accessory.js +441 -122
- package/build/src/lib/accessory.js.map +1 -1
- package/build/src/lib/api.js +177 -232
- package/build/src/lib/api.js.map +1 -1
- package/build/src/lib/detect.js +188 -240
- package/build/src/lib/detect.js.map +1 -1
- package/build/src/lib/items.js +337 -0
- package/build/src/lib/items.js.map +1 -0
- package/build/src/lib/platform.js +198 -38
- package/build/src/lib/platform.js.map +1 -1
- package/build/src/lib/regex.js +2 -2
- package/build/src/lib/regex.js.map +1 -1
- package/build/src/lib/schema.js +4 -3
- package/build/src/lib/schema.js.map +1 -1
- package/build/src/lib/utility.js +277 -38
- package/build/src/lib/utility.js.map +1 -1
- package/build/src/scripts/repl.js +14 -8
- package/build/src/scripts/repl.js.map +1 -1
- package/build/src/scripts/test-api.js +6 -6
- package/build/src/scripts/test-api.js.map +1 -1
- package/config.schema.json +18 -13
- package/package.json +25 -15
package/build/src/lib/api.js
CHANGED
|
@@ -5,8 +5,8 @@ import _ from 'lodash';
|
|
|
5
5
|
import { serializeError } from 'serialize-error';
|
|
6
6
|
import { CookieJar } from 'tough-cookie';
|
|
7
7
|
import { detectedNewDoSubmitHandlers, detectedNewGatewayInformation, detectedNewOrbSecurityButtons, detectedNewPanelInformation, detectedNewPanelStatus, detectedNewPortalVersion, detectedNewSensorsInformation, detectedNewSensorsStatus, } from './detect.js';
|
|
8
|
-
import { paramNetworkId, paramSat, requestPathAccessSignIn,
|
|
9
|
-
import { debugLog, fetchErrorMessage, fetchMissingSatCode, fetchTableCells, findNullKeys, generateDynatracePCHeaderValue, generateHash, isPortalSyncCode, parseArmDisarmMessage, parseDoSubmitHandlers, parseOrbSecurityButtons, parseOrbSensors, parseOrbTextSummary, parseSensorsTable, sleep, stackTracer, } from './utility.js';
|
|
8
|
+
import { paramNetworkId, paramSat, requestPathAccessSignIn, requestPathAccessSignInEXxPartnerAdt, requestPathAccessSignInNetworkIdXxPartnerAdt, requestPathAjaxSyncCheckServTXx, requestPathKeepAlive, requestPathMfaMfaSignInWorkflowChallenge, requestPathQuickControlArmDisarm, requestPathQuickControlServRunRraCommand, requestPathSummarySummary, requestPathSystemDeviceId1, requestPathSystemGateway, requestPathSystemSystem, textPanelEmergencyKeys, } from './regex.js';
|
|
9
|
+
import { debugLog, fetchErrorMessage, fetchMissingSatCode, fetchTableCells, findNullKeys, generateDynatracePCHeaderValue, generateFakeReadyButtons, generateHash, isPortalSyncCode, parseArmDisarmMessage, parseDoSubmitHandlers, parseOrbSecurityButtons, parseOrbSensors, parseOrbTextSummary, parseSensorsTable, sleep, stackTracer, } from './utility.js';
|
|
10
10
|
export class ADTPulse {
|
|
11
11
|
#credentials;
|
|
12
12
|
#internal;
|
|
@@ -25,19 +25,39 @@ export class ADTPulse {
|
|
|
25
25
|
reportedHashes: [],
|
|
26
26
|
testMode: {
|
|
27
27
|
enabled: internalConfig.testMode?.enabled ?? false,
|
|
28
|
-
|
|
28
|
+
isSystemDisarmedBeforeTest: internalConfig.testMode?.isSystemDisarmedBeforeTest ?? false,
|
|
29
29
|
},
|
|
30
|
+
waitTimeAfterArm: 5000,
|
|
30
31
|
};
|
|
31
32
|
this.#session = {
|
|
32
33
|
backupSatCode: null,
|
|
33
34
|
httpClient: wrapper(axios.create({
|
|
34
35
|
jar: new CookieJar(),
|
|
36
|
+
validateStatus: () => true,
|
|
35
37
|
})),
|
|
36
38
|
isAuthenticated: false,
|
|
37
39
|
isCleanState: true,
|
|
38
40
|
networkId: null,
|
|
39
41
|
portalVersion: null,
|
|
40
42
|
};
|
|
43
|
+
if (config.speed !== 1) {
|
|
44
|
+
if (this.#internal.debug) {
|
|
45
|
+
debugLog(this.#internal.logger, 'api.ts / ADTPulse.constructor()', 'warn', `Plugin is now running under ${config.speed}x operational speed. You may see slower device updates`);
|
|
46
|
+
}
|
|
47
|
+
switch (config.speed) {
|
|
48
|
+
case 0.75:
|
|
49
|
+
this.#internal.waitTimeAfterArm = 6000;
|
|
50
|
+
break;
|
|
51
|
+
case 0.5:
|
|
52
|
+
this.#internal.waitTimeAfterArm = 7000;
|
|
53
|
+
break;
|
|
54
|
+
case 0.25:
|
|
55
|
+
this.#internal.waitTimeAfterArm = 8000;
|
|
56
|
+
break;
|
|
57
|
+
default:
|
|
58
|
+
break;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
41
61
|
}
|
|
42
62
|
async login() {
|
|
43
63
|
let errorObject;
|
|
@@ -45,15 +65,7 @@ export class ADTPulse {
|
|
|
45
65
|
debugLog(this.#internal.logger, 'api.ts / ADTPulse.login()', 'info', `Attempting to login to "${this.#internal.baseUrl}"`);
|
|
46
66
|
}
|
|
47
67
|
try {
|
|
48
|
-
const internet = await this.isPortalAccessible();
|
|
49
68
|
const sessions = {};
|
|
50
|
-
if (!internet.success) {
|
|
51
|
-
return {
|
|
52
|
-
action: 'LOGIN',
|
|
53
|
-
success: false,
|
|
54
|
-
info: internet.info,
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
69
|
if (this.isAuthenticated()) {
|
|
58
70
|
if (this.#internal.debug) {
|
|
59
71
|
debugLog(this.#internal.logger, 'api.ts / ADTPulse.login()', 'info', [
|
|
@@ -224,15 +236,7 @@ export class ADTPulse {
|
|
|
224
236
|
debugLog(this.#internal.logger, 'api.ts / ADTPulse.logout()', 'info', `Attempting to logout of "${this.#internal.baseUrl}"`);
|
|
225
237
|
}
|
|
226
238
|
try {
|
|
227
|
-
const internet = await this.isPortalAccessible();
|
|
228
239
|
const sessions = {};
|
|
229
|
-
if (!internet.success) {
|
|
230
|
-
return {
|
|
231
|
-
action: 'LOGOUT',
|
|
232
|
-
success: false,
|
|
233
|
-
info: internet.info,
|
|
234
|
-
};
|
|
235
|
-
}
|
|
236
240
|
if (!this.isAuthenticated()) {
|
|
237
241
|
if (this.#internal.debug) {
|
|
238
242
|
debugLog(this.#internal.logger, 'api.ts / ADTPulse.logout()', 'info', [
|
|
@@ -341,15 +345,7 @@ export class ADTPulse {
|
|
|
341
345
|
debugLog(this.#internal.logger, 'api.ts / ADTPulse.getGatewayInformation()', 'info', `Attempting to retrieve gateway information from "${this.#internal.baseUrl}"`);
|
|
342
346
|
}
|
|
343
347
|
try {
|
|
344
|
-
const internet = await this.isPortalAccessible();
|
|
345
348
|
const sessions = {};
|
|
346
|
-
if (!internet.success) {
|
|
347
|
-
return {
|
|
348
|
-
action: 'GET_GATEWAY_INFORMATION',
|
|
349
|
-
success: false,
|
|
350
|
-
info: internet.info,
|
|
351
|
-
};
|
|
352
|
-
}
|
|
353
349
|
sessions.axiosSystemGateway = await this.#session.httpClient.get(`${this.#internal.baseUrl}/myhome/${this.#session.portalVersion}/system/gateway.jsp`, this.getRequestConfig({
|
|
354
350
|
headers: {
|
|
355
351
|
Referer: `${this.#internal.baseUrl}/myhome/${this.#session.portalVersion}/system/system.jsp`,
|
|
@@ -429,10 +425,10 @@ export class ADTPulse {
|
|
|
429
425
|
], 1, 1);
|
|
430
426
|
const gatewayInformation = {
|
|
431
427
|
communication: {
|
|
432
|
-
primaryConnectionType: _.get(fetchedTableCells, ['Primary Connection Type:', 0], null),
|
|
433
428
|
broadbandConnectionStatus: _.get(fetchedTableCells, ['Broadband Connection Status:', 0], null),
|
|
434
429
|
cellularConnectionStatus: _.get(fetchedTableCells, ['Cellular Connection Status:', 0], null),
|
|
435
430
|
cellularSignalStrength: _.get(fetchedTableCells, ['Cellular Signal Strength:', 0], null),
|
|
431
|
+
primaryConnectionType: _.get(fetchedTableCells, ['Primary Connection Type:', 0], null),
|
|
436
432
|
},
|
|
437
433
|
manufacturer: _.get(fetchedTableCells, ['Manufacturer:', 0], null),
|
|
438
434
|
model: _.get(fetchedTableCells, ['Model:', 0], null),
|
|
@@ -492,15 +488,7 @@ export class ADTPulse {
|
|
|
492
488
|
debugLog(this.#internal.logger, 'api.ts / ADTPulse.getPanelInformation()', 'info', `Attempting to retrieve panel information from "${this.#internal.baseUrl}"`);
|
|
493
489
|
}
|
|
494
490
|
try {
|
|
495
|
-
const internet = await this.isPortalAccessible();
|
|
496
491
|
const sessions = {};
|
|
497
|
-
if (!internet.success) {
|
|
498
|
-
return {
|
|
499
|
-
action: 'GET_PANEL_INFORMATION',
|
|
500
|
-
success: false,
|
|
501
|
-
info: internet.info,
|
|
502
|
-
};
|
|
503
|
-
}
|
|
504
492
|
sessions.axiosSystemDeviceId1 = await this.#session.httpClient.get(`${this.#internal.baseUrl}/myhome/${this.#session.portalVersion}/system/device.jsp?id=1`, this.getRequestConfig({
|
|
505
493
|
headers: {
|
|
506
494
|
Referer: `${this.#internal.baseUrl}/myhome/${this.#session.portalVersion}/system/system.jsp`,
|
|
@@ -565,13 +553,13 @@ export class ADTPulse {
|
|
|
565
553
|
'Type/Model:',
|
|
566
554
|
], 1, 1);
|
|
567
555
|
const emergencyKeys = _.get(fetchedTableCells, ['Emergency Keys:', 0], null);
|
|
568
|
-
const parsedEmergencyKeys = (
|
|
556
|
+
const parsedEmergencyKeys = (emergencyKeys !== null) ? emergencyKeys.match(textPanelEmergencyKeys) : null;
|
|
569
557
|
const manufacturerProvider = _.get(fetchedTableCells, ['Manufacturer/Provider:', 0], null);
|
|
570
|
-
const parsedManufacturer = (
|
|
571
|
-
const parsedProvider = (
|
|
558
|
+
const parsedManufacturer = (manufacturerProvider !== null) ? manufacturerProvider.split(' - ')[0] ?? null : null;
|
|
559
|
+
const parsedProvider = (manufacturerProvider !== null) ? manufacturerProvider.split(' - ')[1] ?? null : null;
|
|
572
560
|
const typeModel = _.get(fetchedTableCells, ['Type/Model:', 0], null);
|
|
573
|
-
const parsedType = (
|
|
574
|
-
const parsedModel = (
|
|
561
|
+
const parsedType = (typeModel !== null) ? typeModel.split(' - ')[0] ?? null : null;
|
|
562
|
+
const parsedModel = (typeModel !== null) ? typeModel.split(' - ')[1] ?? null : null;
|
|
575
563
|
const panelInformation = {
|
|
576
564
|
emergencyKeys: parsedEmergencyKeys,
|
|
577
565
|
manufacturer: parsedManufacturer,
|
|
@@ -612,15 +600,7 @@ export class ADTPulse {
|
|
|
612
600
|
debugLog(this.#internal.logger, 'api.ts / ADTPulse.getPanelStatus()', 'info', `Attempting to retrieve panel status from "${this.#internal.baseUrl}"`);
|
|
613
601
|
}
|
|
614
602
|
try {
|
|
615
|
-
const internet = await this.isPortalAccessible();
|
|
616
603
|
const sessions = {};
|
|
617
|
-
if (!internet.success) {
|
|
618
|
-
return {
|
|
619
|
-
action: 'GET_PANEL_STATUS',
|
|
620
|
-
success: false,
|
|
621
|
-
info: internet.info,
|
|
622
|
-
};
|
|
623
|
-
}
|
|
624
604
|
sessions.axiosSummary = await this.#session.httpClient.get(`${this.#internal.baseUrl}/myhome/${this.#session.portalVersion}/summary/summary.jsp`, this.getRequestConfig({
|
|
625
605
|
headers: {
|
|
626
606
|
Referer: `${this.#internal.baseUrl}/myhome/${this.#session.portalVersion}/summary/summary.jsp`,
|
|
@@ -712,10 +692,22 @@ export class ADTPulse {
|
|
|
712
692
|
},
|
|
713
693
|
};
|
|
714
694
|
}
|
|
715
|
-
async setPanelStatus(armTo) {
|
|
695
|
+
async setPanelStatus(armFrom, armTo, isAlarmActive) {
|
|
716
696
|
let errorObject;
|
|
717
697
|
if (this.#internal.debug) {
|
|
718
|
-
debugLog(this.#internal.logger, 'api.ts / ADTPulse.setPanelStatus()', 'info', `Attempting to update panel status to "${armTo}" at "${this.#internal.baseUrl}"`);
|
|
698
|
+
debugLog(this.#internal.logger, 'api.ts / ADTPulse.setPanelStatus()', 'info', `Attempting to update panel status from "${armFrom}" to "${armTo}" at "${this.#internal.baseUrl}"`);
|
|
699
|
+
}
|
|
700
|
+
if (armFrom !== 'away'
|
|
701
|
+
&& armFrom !== 'night'
|
|
702
|
+
&& armFrom !== 'off'
|
|
703
|
+
&& armFrom !== 'stay') {
|
|
704
|
+
return {
|
|
705
|
+
action: 'SET_PANEL_STATUS',
|
|
706
|
+
success: false,
|
|
707
|
+
info: {
|
|
708
|
+
message: `"${armFrom}" is an invalid arm from state`,
|
|
709
|
+
},
|
|
710
|
+
};
|
|
719
711
|
}
|
|
720
712
|
if (armTo !== 'away'
|
|
721
713
|
&& armTo !== 'night'
|
|
@@ -729,16 +721,41 @@ export class ADTPulse {
|
|
|
729
721
|
},
|
|
730
722
|
};
|
|
731
723
|
}
|
|
724
|
+
if (typeof isAlarmActive !== 'boolean') {
|
|
725
|
+
return {
|
|
726
|
+
action: 'SET_PANEL_STATUS',
|
|
727
|
+
success: false,
|
|
728
|
+
info: {
|
|
729
|
+
message: 'You must specify if the system\'s alarm is currently ringing (true) or not (false)',
|
|
730
|
+
},
|
|
731
|
+
};
|
|
732
|
+
}
|
|
733
|
+
if ((armFrom === 'away'
|
|
734
|
+
&& armTo === 'away')
|
|
735
|
+
|| (armFrom === 'night'
|
|
736
|
+
&& armTo === 'night')
|
|
737
|
+
|| (armFrom === 'stay'
|
|
738
|
+
&& armTo === 'stay')
|
|
739
|
+
|| (armFrom === 'off'
|
|
740
|
+
&& armTo === 'off'
|
|
741
|
+
&& !isAlarmActive)) {
|
|
742
|
+
if (this.#internal.debug) {
|
|
743
|
+
debugLog(this.#internal.logger, 'api.ts / ADTPulse.setPanelStatus()', 'info', `No need to change arm state from "${armFrom}" to "${armTo}" due to its equivalence`);
|
|
744
|
+
}
|
|
745
|
+
return {
|
|
746
|
+
action: 'SET_PANEL_STATUS',
|
|
747
|
+
success: true,
|
|
748
|
+
info: {
|
|
749
|
+
forceArmRequired: false,
|
|
750
|
+
},
|
|
751
|
+
};
|
|
752
|
+
}
|
|
753
|
+
if (this.#internal.debug && isAlarmActive) {
|
|
754
|
+
debugLog(this.#internal.logger, 'api.ts / ADTPulse.setPanelStatus()', 'warn', `Alarm is currently ringing and arm state is being changed from "${armFrom}" to "${armTo}"`);
|
|
755
|
+
}
|
|
732
756
|
try {
|
|
733
|
-
const internet = await this.isPortalAccessible();
|
|
734
757
|
const sessions = {};
|
|
735
|
-
|
|
736
|
-
return {
|
|
737
|
-
action: 'SET_PANEL_STATUS',
|
|
738
|
-
success: false,
|
|
739
|
-
info: internet.info,
|
|
740
|
-
};
|
|
741
|
-
}
|
|
758
|
+
let isAlarmCurrentlyActive = isAlarmActive;
|
|
742
759
|
sessions.axiosSummary = await this.#session.httpClient.get(`${this.#internal.baseUrl}/myhome/${this.#session.portalVersion}/summary/summary.jsp`, this.getRequestConfig({
|
|
743
760
|
headers: {
|
|
744
761
|
Referer: `${this.#internal.baseUrl}/myhome/${this.#session.portalVersion}/summary/summary.jsp`,
|
|
@@ -806,34 +823,33 @@ export class ADTPulse {
|
|
|
806
823
|
const jsdomSummaryOrbSecurityButtons = sessions.jsdomSummary.window.document.querySelectorAll('#divOrbSecurityButtons input');
|
|
807
824
|
const parsedOrbSecurityButtons = parseOrbSecurityButtons(jsdomSummaryOrbSecurityButtons);
|
|
808
825
|
await this.newInformationDispatcher('orb-security-buttons', parsedOrbSecurityButtons);
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
return (parsedOrbSecurityButtonButtonDisabled && parsedOrbSecurityButtonButtonText === 'Arming Night');
|
|
813
|
-
});
|
|
814
|
-
if (this.#session.backupSatCode !== null
|
|
815
|
-
&& armingNightButtonIndex >= 0) {
|
|
816
|
-
if (this.#internal.debug) {
|
|
817
|
-
debugLog(this.#internal.logger, 'api.ts / ADTPulse.setPanelStatus()', 'warn', 'Replacing the stuck "Arming Night" button with a fake "Disarm" button');
|
|
818
|
-
}
|
|
819
|
-
parsedOrbSecurityButtons[armingNightButtonIndex] = {
|
|
820
|
-
buttonDisabled: false,
|
|
821
|
-
buttonId: 'security_button_0',
|
|
822
|
-
buttonIndex: 0,
|
|
823
|
-
buttonText: 'Disarm',
|
|
824
|
-
changeAccessCode: false,
|
|
825
|
-
loadingText: 'Disarming',
|
|
826
|
+
let readyButtons = parsedOrbSecurityButtons.filter((parsedOrbSecurityButton) => !parsedOrbSecurityButton.buttonDisabled);
|
|
827
|
+
if (readyButtons.length === 0 && this.#session.backupSatCode !== null) {
|
|
828
|
+
readyButtons = generateFakeReadyButtons(parsedOrbSecurityButtons, this.#session.isCleanState, {
|
|
826
829
|
relativeUrl: 'quickcontrol/armDisarm.jsp',
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
830
|
+
href: 'rest/adt/ui/client/security/setArmState',
|
|
831
|
+
sat: this.#session.backupSatCode,
|
|
832
|
+
});
|
|
833
|
+
if (this.#internal.debug) {
|
|
834
|
+
debugLog(this.#internal.logger, 'api.ts / ADTPulse.setPanelStatus()', 'warn', 'No security buttons were found. Replacing stuck orb security buttons with fake buttons');
|
|
835
|
+
stackTracer('fake-ready-buttons', {
|
|
836
|
+
before: parsedOrbSecurityButtons,
|
|
837
|
+
after: readyButtons,
|
|
838
|
+
});
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
if (readyButtons.length === 0 && this.#session.backupSatCode === null) {
|
|
842
|
+
if (this.#internal.debug) {
|
|
843
|
+
debugLog(this.#internal.logger, 'api.ts / ADTPulse.setPanelStatus()', 'error', 'No security buttons were found and replacement failed because no backup sat code exists');
|
|
844
|
+
}
|
|
845
|
+
return {
|
|
846
|
+
action: 'SET_PANEL_STATUS',
|
|
847
|
+
success: false,
|
|
848
|
+
info: {
|
|
849
|
+
message: 'No security buttons were found and replacement failed because no backup sat code exists',
|
|
833
850
|
},
|
|
834
851
|
};
|
|
835
852
|
}
|
|
836
|
-
let readyButtons = parsedOrbSecurityButtons.filter((parsedOrbSecurityButton) => !parsedOrbSecurityButton.buttonDisabled);
|
|
837
853
|
if (readyButtons.length < 1) {
|
|
838
854
|
if (this.#internal.debug) {
|
|
839
855
|
debugLog(this.#internal.logger, 'api.ts / ADTPulse.setPanelStatus()', 'error', 'Security buttons are not found on the summary page');
|
|
@@ -847,7 +863,7 @@ export class ADTPulse {
|
|
|
847
863
|
};
|
|
848
864
|
}
|
|
849
865
|
if (this.#internal.testMode.enabled
|
|
850
|
-
&& !this.#internal.testMode.
|
|
866
|
+
&& !this.#internal.testMode.isSystemDisarmedBeforeTest) {
|
|
851
867
|
if (!['off', 'disarmed'].includes(readyButtons[0].urlParams.armState)) {
|
|
852
868
|
if (this.#internal.debug) {
|
|
853
869
|
debugLog(this.#internal.logger, 'api.ts / ADTPulse.setPanelStatus()', 'error', 'Test mode is active and system is not disarmed');
|
|
@@ -860,10 +876,16 @@ export class ADTPulse {
|
|
|
860
876
|
},
|
|
861
877
|
};
|
|
862
878
|
}
|
|
863
|
-
this.#internal.testMode.
|
|
879
|
+
this.#internal.testMode.isSystemDisarmedBeforeTest = true;
|
|
864
880
|
}
|
|
865
|
-
while (!['off', 'disarmed'].includes(readyButtons[0].urlParams.armState)) {
|
|
866
|
-
const armDisarmResponse = await this.armDisarmHandler(
|
|
881
|
+
while (isAlarmCurrentlyActive || !['off', 'disarmed'].includes(readyButtons[0].urlParams.armState)) {
|
|
882
|
+
const armDisarmResponse = await this.armDisarmHandler(isAlarmCurrentlyActive, {
|
|
883
|
+
relativeUrl: readyButtons[0].relativeUrl,
|
|
884
|
+
href: readyButtons[0].urlParams.href,
|
|
885
|
+
armState: readyButtons[0].urlParams.armState,
|
|
886
|
+
arm: 'off',
|
|
887
|
+
sat: readyButtons[0].urlParams.sat,
|
|
888
|
+
});
|
|
867
889
|
if (!armDisarmResponse.success) {
|
|
868
890
|
if (this.#internal.debug) {
|
|
869
891
|
debugLog(this.#internal.logger, 'api.ts / ADTPulse.setPanelStatus()', 'error', 'An error occurred in the arm disarm handler (while disarming)');
|
|
@@ -887,10 +909,19 @@ export class ADTPulse {
|
|
|
887
909
|
};
|
|
888
910
|
}
|
|
889
911
|
readyButtons = armDisarmResponse.info.readyButtons;
|
|
912
|
+
if (isAlarmCurrentlyActive) {
|
|
913
|
+
isAlarmCurrentlyActive = false;
|
|
914
|
+
}
|
|
890
915
|
}
|
|
891
916
|
let forceArmRequired = false;
|
|
892
917
|
if (armTo !== 'off') {
|
|
893
|
-
const armDisarmResponse = await this.armDisarmHandler(
|
|
918
|
+
const armDisarmResponse = await this.armDisarmHandler(false, {
|
|
919
|
+
relativeUrl: readyButtons[0].relativeUrl,
|
|
920
|
+
href: readyButtons[0].urlParams.href,
|
|
921
|
+
armState: readyButtons[0].urlParams.armState,
|
|
922
|
+
arm: armTo,
|
|
923
|
+
sat: readyButtons[0].urlParams.sat,
|
|
924
|
+
});
|
|
894
925
|
if (!armDisarmResponse.success) {
|
|
895
926
|
if (this.#internal.debug) {
|
|
896
927
|
debugLog(this.#internal.logger, 'api.ts / ADTPulse.setPanelStatus()', 'error', 'An error occurred in the arm disarm handler (while arming)');
|
|
@@ -904,7 +935,7 @@ export class ADTPulse {
|
|
|
904
935
|
forceArmRequired = armDisarmResponse.info.forceArmRequired;
|
|
905
936
|
}
|
|
906
937
|
if (this.#internal.debug) {
|
|
907
|
-
debugLog(this.#internal.logger, 'api.ts / ADTPulse.setPanelStatus()', 'success', `Successfully updated panel status to "${armTo}" at "${this.#internal.baseUrl}"`);
|
|
938
|
+
debugLog(this.#internal.logger, 'api.ts / ADTPulse.setPanelStatus()', 'success', `Successfully updated panel status from "${armFrom}" to "${armTo}" at "${this.#internal.baseUrl}"`);
|
|
908
939
|
}
|
|
909
940
|
return {
|
|
910
941
|
action: 'SET_PANEL_STATUS',
|
|
@@ -935,15 +966,7 @@ export class ADTPulse {
|
|
|
935
966
|
debugLog(this.#internal.logger, 'api.ts / ADTPulse.getSensorsInformation()', 'info', `Attempting to retrieve sensors information from "${this.#internal.baseUrl}"`);
|
|
936
967
|
}
|
|
937
968
|
try {
|
|
938
|
-
const internet = await this.isPortalAccessible();
|
|
939
969
|
const sessions = {};
|
|
940
|
-
if (!internet.success) {
|
|
941
|
-
return {
|
|
942
|
-
action: 'GET_SENSORS_INFORMATION',
|
|
943
|
-
success: false,
|
|
944
|
-
info: internet.info,
|
|
945
|
-
};
|
|
946
|
-
}
|
|
947
970
|
sessions.axiosSystem = await this.#session.httpClient.get(`${this.#internal.baseUrl}/myhome/${this.#session.portalVersion}/system/system.jsp`, this.getRequestConfig({
|
|
948
971
|
headers: {
|
|
949
972
|
Referer: `${this.#internal.baseUrl}/myhome/${this.#session.portalVersion}/summary/summary.jsp`,
|
|
@@ -999,7 +1022,7 @@ export class ADTPulse {
|
|
|
999
1022
|
contentType: 'text/html',
|
|
1000
1023
|
pretendToBeVisual: true,
|
|
1001
1024
|
});
|
|
1002
|
-
const jsdomSystemSensorsTable = sessions.jsdomSystem.window.document.querySelectorAll('#systemContentList tr[
|
|
1025
|
+
const jsdomSystemSensorsTable = sessions.jsdomSystem.window.document.querySelectorAll('#systemContentList tr[class^=\'p_row\'] tr.p_listRow');
|
|
1003
1026
|
const parsedSensorsTable = parseSensorsTable(jsdomSystemSensorsTable);
|
|
1004
1027
|
await this.newInformationDispatcher('sensors-information', parsedSensorsTable);
|
|
1005
1028
|
if (this.#internal.debug) {
|
|
@@ -1034,15 +1057,7 @@ export class ADTPulse {
|
|
|
1034
1057
|
debugLog(this.#internal.logger, 'api.ts / ADTPulse.getSensorsStatus()', 'info', `Attempting to retrieve sensors status from "${this.#internal.baseUrl}"`);
|
|
1035
1058
|
}
|
|
1036
1059
|
try {
|
|
1037
|
-
const internet = await this.isPortalAccessible();
|
|
1038
1060
|
const sessions = {};
|
|
1039
|
-
if (!internet.success) {
|
|
1040
|
-
return {
|
|
1041
|
-
action: 'GET_SENSORS_STATUS',
|
|
1042
|
-
success: false,
|
|
1043
|
-
info: internet.info,
|
|
1044
|
-
};
|
|
1045
|
-
}
|
|
1046
1061
|
sessions.axiosSummary = await this.#session.httpClient.get(`${this.#internal.baseUrl}/myhome/${this.#session.portalVersion}/summary/summary.jsp`, this.getRequestConfig({
|
|
1047
1062
|
headers: {
|
|
1048
1063
|
Referer: `${this.#internal.baseUrl}/myhome/${this.#session.portalVersion}/summary/summary.jsp`,
|
|
@@ -1142,15 +1157,7 @@ export class ADTPulse {
|
|
|
1142
1157
|
debugLog(this.#internal.logger, 'api.ts / ADTPulse.performSyncCheck()', 'info', `Attempting to perform a sync check from "${this.#internal.baseUrl}"`);
|
|
1143
1158
|
}
|
|
1144
1159
|
try {
|
|
1145
|
-
const internet = await this.isPortalAccessible();
|
|
1146
1160
|
const sessions = {};
|
|
1147
|
-
if (!internet.success) {
|
|
1148
|
-
return {
|
|
1149
|
-
action: 'PERFORM_SYNC_CHECK',
|
|
1150
|
-
success: false,
|
|
1151
|
-
info: internet.info,
|
|
1152
|
-
};
|
|
1153
|
-
}
|
|
1154
1161
|
sessions.axiosSyncCheck = await this.#session.httpClient.get(`${this.#internal.baseUrl}/myhome/${this.#session.portalVersion}/Ajax/SyncCheckServ?t=${Date.now()}`, this.getRequestConfig({
|
|
1155
1162
|
headers: {
|
|
1156
1163
|
Accept: '*/*',
|
|
@@ -1249,15 +1256,7 @@ export class ADTPulse {
|
|
|
1249
1256
|
debugLog(this.#internal.logger, 'api.ts / ADTPulse.performKeepAlive()', 'info', `Attempting to perform a keep alive from "${this.#internal.baseUrl}"`);
|
|
1250
1257
|
}
|
|
1251
1258
|
try {
|
|
1252
|
-
const internet = await this.isPortalAccessible();
|
|
1253
1259
|
const sessions = {};
|
|
1254
|
-
if (!internet.success) {
|
|
1255
|
-
return {
|
|
1256
|
-
action: 'PERFORM_KEEP_ALIVE',
|
|
1257
|
-
success: false,
|
|
1258
|
-
info: internet.info,
|
|
1259
|
-
};
|
|
1260
|
-
}
|
|
1261
1260
|
sessions.axiosKeepAlive = await this.#session.httpClient.post(`${this.#internal.baseUrl}/myhome/${this.#session.portalVersion}/KeepAlive`, '', this.getRequestConfig({
|
|
1262
1261
|
headers: {
|
|
1263
1262
|
Accept: '*/*',
|
|
@@ -1330,16 +1329,40 @@ export class ADTPulse {
|
|
|
1330
1329
|
isAuthenticated() {
|
|
1331
1330
|
return this.#session.isAuthenticated;
|
|
1332
1331
|
}
|
|
1333
|
-
|
|
1332
|
+
resetSession() {
|
|
1333
|
+
this.#session = {
|
|
1334
|
+
backupSatCode: null,
|
|
1335
|
+
httpClient: wrapper(axios.create({
|
|
1336
|
+
jar: new CookieJar(),
|
|
1337
|
+
validateStatus: () => true,
|
|
1338
|
+
})),
|
|
1339
|
+
isAuthenticated: false,
|
|
1340
|
+
isCleanState: true,
|
|
1341
|
+
networkId: null,
|
|
1342
|
+
portalVersion: null,
|
|
1343
|
+
};
|
|
1344
|
+
}
|
|
1345
|
+
async armDisarmHandler(isAlarmActive, options) {
|
|
1334
1346
|
let errorObject;
|
|
1335
1347
|
if (this.#internal.debug) {
|
|
1336
|
-
debugLog(this.#internal.logger, 'api.ts / ADTPulse.armDisarmHandler()', 'info', `Attempting to update arm state from "${armState}" to "${arm}" on "${this.#internal.baseUrl}"`);
|
|
1348
|
+
debugLog(this.#internal.logger, 'api.ts / ADTPulse.armDisarmHandler()', 'info', `Attempting to update arm state from "${options.armState}" to "${options.arm}" on "${this.#internal.baseUrl}"`);
|
|
1337
1349
|
}
|
|
1338
|
-
if (armState ===
|
|
1339
|
-
|
|
1340
|
-
|
|
1350
|
+
if ((options.armState === 'away'
|
|
1351
|
+
&& options.arm === 'away')
|
|
1352
|
+
|| (options.armState === 'night'
|
|
1353
|
+
&& options.arm === 'night')
|
|
1354
|
+
|| (options.armState === 'night+stay'
|
|
1355
|
+
&& options.arm === 'night')
|
|
1356
|
+
|| (options.armState === 'stay'
|
|
1357
|
+
&& options.arm === 'stay')
|
|
1358
|
+
|| (options.armState === 'disarmed'
|
|
1359
|
+
&& options.arm === 'off'
|
|
1360
|
+
&& !isAlarmActive)
|
|
1361
|
+
|| (options.armState === 'off'
|
|
1362
|
+
&& options.arm === 'off'
|
|
1363
|
+
&& !isAlarmActive)) {
|
|
1341
1364
|
if (this.#internal.debug) {
|
|
1342
|
-
debugLog(this.#internal.logger, 'api.ts / ADTPulse.armDisarmHandler()', 'info', `No need to change arm state from "${armState}" to "${arm}" due to its equivalence`);
|
|
1365
|
+
debugLog(this.#internal.logger, 'api.ts / ADTPulse.armDisarmHandler()', 'info', `No need to change arm state from "${options.armState}" to "${options.arm}" due to its equivalence`);
|
|
1343
1366
|
}
|
|
1344
1367
|
return {
|
|
1345
1368
|
action: 'ARM_DISARM_HANDLER',
|
|
@@ -1351,21 +1374,13 @@ export class ADTPulse {
|
|
|
1351
1374
|
};
|
|
1352
1375
|
}
|
|
1353
1376
|
try {
|
|
1354
|
-
const internet = await this.isPortalAccessible();
|
|
1355
1377
|
const sessions = {};
|
|
1356
|
-
if (!internet.success) {
|
|
1357
|
-
return {
|
|
1358
|
-
action: 'ARM_DISARM_HANDLER',
|
|
1359
|
-
success: false,
|
|
1360
|
-
info: internet.info,
|
|
1361
|
-
};
|
|
1362
|
-
}
|
|
1363
1378
|
const armDisarmForm = new URLSearchParams();
|
|
1364
|
-
armDisarmForm.append('href', href);
|
|
1365
|
-
armDisarmForm.append('armstate', armState);
|
|
1366
|
-
armDisarmForm.append('arm', arm);
|
|
1367
|
-
armDisarmForm.append('sat', sat);
|
|
1368
|
-
sessions.axiosSetArmMode = await this.#session.httpClient.post(`${this.#internal.baseUrl}/myhome/${this.#session.portalVersion}/${relativeUrl}`, armDisarmForm, this.getRequestConfig({
|
|
1379
|
+
armDisarmForm.append('href', options.href);
|
|
1380
|
+
armDisarmForm.append('armstate', options.armState);
|
|
1381
|
+
armDisarmForm.append('arm', options.arm);
|
|
1382
|
+
armDisarmForm.append('sat', options.sat);
|
|
1383
|
+
sessions.axiosSetArmMode = await this.#session.httpClient.post(`${this.#internal.baseUrl}/myhome/${this.#session.portalVersion}/${options.relativeUrl}`, armDisarmForm, this.getRequestConfig({
|
|
1369
1384
|
headers: {
|
|
1370
1385
|
'Cache-Control': 'max-age=0',
|
|
1371
1386
|
'Content-Type': 'application/x-www-form-urlencoded',
|
|
@@ -1409,8 +1424,8 @@ export class ADTPulse {
|
|
|
1409
1424
|
};
|
|
1410
1425
|
}
|
|
1411
1426
|
let forceArmRequired = false;
|
|
1412
|
-
if (arm !== 'off') {
|
|
1413
|
-
const forceArmResponse = await this.forceArmHandler(sessions.axiosSetArmMode, relativeUrl);
|
|
1427
|
+
if (options.arm !== 'off') {
|
|
1428
|
+
const forceArmResponse = await this.forceArmHandler(sessions.axiosSetArmMode, options.relativeUrl);
|
|
1414
1429
|
if (!forceArmResponse.success) {
|
|
1415
1430
|
if (this.#internal.debug) {
|
|
1416
1431
|
debugLog(this.#internal.logger, 'api.ts / ADTPulse.armDisarmHandler()', 'error', 'An error occurred in the force arm handler');
|
|
@@ -1424,7 +1439,7 @@ export class ADTPulse {
|
|
|
1424
1439
|
forceArmRequired = forceArmResponse.info.forceArmRequired;
|
|
1425
1440
|
}
|
|
1426
1441
|
this.#session.isCleanState = false;
|
|
1427
|
-
await sleep(
|
|
1442
|
+
await sleep(this.#internal.waitTimeAfterArm);
|
|
1428
1443
|
sessions.axiosSummary = await this.#session.httpClient.get(`${this.#internal.baseUrl}/myhome/${this.#session.portalVersion}/summary/summary.jsp`, this.getRequestConfig({
|
|
1429
1444
|
headers: {
|
|
1430
1445
|
Referer: `${this.#internal.baseUrl}/myhome/${this.#session.portalVersion}/summary/summary.jsp`,
|
|
@@ -1493,30 +1508,22 @@ export class ADTPulse {
|
|
|
1493
1508
|
const parsedOrbSecurityButtons = parseOrbSecurityButtons(jsdomSummaryOrbSecurityButtons);
|
|
1494
1509
|
await this.newInformationDispatcher('orb-security-buttons', parsedOrbSecurityButtons);
|
|
1495
1510
|
let readyButtons = parsedOrbSecurityButtons.filter((parsedOrbSecurityButton) => !parsedOrbSecurityButton.buttonDisabled);
|
|
1496
|
-
if (
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
urlParams: {
|
|
1510
|
-
arm: 'off',
|
|
1511
|
-
armState: (this.#session.isCleanState) ? 'night' : 'night+stay',
|
|
1512
|
-
href,
|
|
1513
|
-
sat,
|
|
1514
|
-
},
|
|
1515
|
-
},
|
|
1516
|
-
];
|
|
1511
|
+
if (readyButtons.length === 0) {
|
|
1512
|
+
readyButtons = generateFakeReadyButtons(parsedOrbSecurityButtons, this.#session.isCleanState, {
|
|
1513
|
+
relativeUrl: options.relativeUrl,
|
|
1514
|
+
href: options.href,
|
|
1515
|
+
sat: options.sat,
|
|
1516
|
+
});
|
|
1517
|
+
if (this.#internal.debug) {
|
|
1518
|
+
debugLog(this.#internal.logger, 'api.ts / ADTPulse.armDisarmHandler()', 'warn', 'No security buttons were found. Replacing stuck orb security buttons with fake buttons');
|
|
1519
|
+
stackTracer('fake-ready-buttons', {
|
|
1520
|
+
before: parsedOrbSecurityButtons,
|
|
1521
|
+
after: readyButtons,
|
|
1522
|
+
});
|
|
1523
|
+
}
|
|
1517
1524
|
}
|
|
1518
1525
|
if (this.#internal.debug) {
|
|
1519
|
-
debugLog(this.#internal.logger, 'api.ts / ADTPulse.armDisarmHandler()', 'success', `Successfully updated arm state from "${armState}" to "${arm}" on "${this.#internal.baseUrl}"`);
|
|
1526
|
+
debugLog(this.#internal.logger, 'api.ts / ADTPulse.armDisarmHandler()', 'success', `Successfully updated arm state from "${options.armState}" to "${options.arm}" on "${this.#internal.baseUrl}"`);
|
|
1520
1527
|
}
|
|
1521
1528
|
return {
|
|
1522
1529
|
action: 'ARM_DISARM_HANDLER',
|
|
@@ -1548,15 +1555,7 @@ export class ADTPulse {
|
|
|
1548
1555
|
debugLog(this.#internal.logger, 'api.ts / ADTPulse.forceArmHandler()', 'info', `Attempting to force arm on "${this.#internal.baseUrl}"`);
|
|
1549
1556
|
}
|
|
1550
1557
|
try {
|
|
1551
|
-
const internet = await this.isPortalAccessible();
|
|
1552
1558
|
const sessions = {};
|
|
1553
|
-
if (!internet.success) {
|
|
1554
|
-
return {
|
|
1555
|
-
action: 'FORCE_ARM_HANDLER',
|
|
1556
|
-
success: false,
|
|
1557
|
-
info: internet.info,
|
|
1558
|
-
};
|
|
1559
|
-
}
|
|
1560
1559
|
if (typeof response.data !== 'string') {
|
|
1561
1560
|
if (this.#internal.debug) {
|
|
1562
1561
|
debugLog(this.#internal.logger, 'api.ts / ADTPulse.forceArmHandler()', 'error', 'The response body of the arm disarm page is not of type "string"');
|
|
@@ -1731,49 +1730,6 @@ export class ADTPulse {
|
|
|
1731
1730
|
},
|
|
1732
1731
|
};
|
|
1733
1732
|
}
|
|
1734
|
-
async isPortalAccessible() {
|
|
1735
|
-
let errorObject;
|
|
1736
|
-
if (this.#internal.debug) {
|
|
1737
|
-
debugLog(this.#internal.logger, 'api.ts / ADTPulse.isPortalAccessible()', 'info', `Attempting to check if "${this.#internal.baseUrl}" is accessible`);
|
|
1738
|
-
}
|
|
1739
|
-
try {
|
|
1740
|
-
const response = await axios.head(this.#internal.baseUrl, this.getRequestConfig());
|
|
1741
|
-
if (response.status !== 200 || response.statusText !== 'OK') {
|
|
1742
|
-
if (this.#internal.debug) {
|
|
1743
|
-
debugLog(this.#internal.logger, 'api.ts / ADTPulse.isPortalAccessible()', 'error', `The portal at "${this.#internal.baseUrl}" is not accessible`);
|
|
1744
|
-
}
|
|
1745
|
-
return {
|
|
1746
|
-
action: 'IS_PORTAL_ACCESSIBLE',
|
|
1747
|
-
success: false,
|
|
1748
|
-
info: {
|
|
1749
|
-
message: `The portal at "${this.#internal.baseUrl}" is not accessible`,
|
|
1750
|
-
},
|
|
1751
|
-
};
|
|
1752
|
-
}
|
|
1753
|
-
if (this.#internal.debug) {
|
|
1754
|
-
debugLog(this.#internal.logger, 'api.ts / ADTPulse.isPortalAccessible()', 'success', `Successfully checked if "${this.#internal.baseUrl}" is accessible`);
|
|
1755
|
-
}
|
|
1756
|
-
return {
|
|
1757
|
-
action: 'IS_PORTAL_ACCESSIBLE',
|
|
1758
|
-
success: true,
|
|
1759
|
-
info: null,
|
|
1760
|
-
};
|
|
1761
|
-
}
|
|
1762
|
-
catch (error) {
|
|
1763
|
-
errorObject = serializeError(error);
|
|
1764
|
-
}
|
|
1765
|
-
if (this.#internal.debug) {
|
|
1766
|
-
debugLog(this.#internal.logger, 'api.ts / ADTPulse.isPortalAccessible()', 'error', 'Method encountered an error during execution');
|
|
1767
|
-
stackTracer('serialize-error', errorObject);
|
|
1768
|
-
}
|
|
1769
|
-
return {
|
|
1770
|
-
action: 'IS_PORTAL_ACCESSIBLE',
|
|
1771
|
-
success: false,
|
|
1772
|
-
info: {
|
|
1773
|
-
error: errorObject,
|
|
1774
|
-
},
|
|
1775
|
-
};
|
|
1776
|
-
}
|
|
1777
1733
|
async newInformationDispatcher(type, data) {
|
|
1778
1734
|
const dataHash = generateHash(`${type}: ${JSON.stringify(data)}`);
|
|
1779
1735
|
if (this.#internal.reportedHashes.find((reportedHash) => dataHash === reportedHash) === undefined) {
|
|
@@ -1818,8 +1774,10 @@ export class ADTPulse {
|
|
|
1818
1774
|
Accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
|
|
1819
1775
|
'Accept-Encoding': 'gzip, deflate, br',
|
|
1820
1776
|
'Accept-Language': 'en-US,en;q=0.9',
|
|
1777
|
+
'Cache-Control': 'no-cache',
|
|
1821
1778
|
Connection: 'keep-alive',
|
|
1822
1779
|
Host: `${this.#credentials.subdomain}.adtpulse.com`,
|
|
1780
|
+
Pragma: 'no-cache',
|
|
1823
1781
|
'Sec-Fetch-Dest': 'document',
|
|
1824
1782
|
'Sec-Fetch-Mode': 'navigate',
|
|
1825
1783
|
'Sec-Fetch-Site': 'none',
|
|
@@ -1831,7 +1789,6 @@ export class ADTPulse {
|
|
|
1831
1789
|
'sec-ch-ua-platform': '"macOS"',
|
|
1832
1790
|
},
|
|
1833
1791
|
timeout: 15000,
|
|
1834
|
-
validateStatus: undefined,
|
|
1835
1792
|
};
|
|
1836
1793
|
if (extraConfig === undefined) {
|
|
1837
1794
|
return defaultConfig;
|
|
@@ -1843,11 +1800,11 @@ export class ADTPulse {
|
|
|
1843
1800
|
return;
|
|
1844
1801
|
}
|
|
1845
1802
|
if (requestPathAccessSignIn.test(requestPath)
|
|
1846
|
-
||
|
|
1803
|
+
|| requestPathAccessSignInEXxPartnerAdt.test(requestPath)
|
|
1847
1804
|
|| requestPathMfaMfaSignInWorkflowChallenge.test(requestPath)) {
|
|
1848
1805
|
if (this.#internal.debug) {
|
|
1849
1806
|
const errorMessage = fetchErrorMessage(session);
|
|
1850
|
-
if (requestPathAccessSignIn.test(requestPath) ||
|
|
1807
|
+
if (requestPathAccessSignIn.test(requestPath) || requestPathAccessSignInEXxPartnerAdt.test(requestPath)) {
|
|
1851
1808
|
debugLog(this.#internal.logger, 'api.ts / ADTPulse.handleLoginFailure()', 'error', 'Either the username or password is incorrect, fingerprint format is invalid, or was signed out due to inactivity');
|
|
1852
1809
|
}
|
|
1853
1810
|
if (requestPathMfaMfaSignInWorkflowChallenge.test(requestPath)) {
|
|
@@ -1860,17 +1817,5 @@ export class ADTPulse {
|
|
|
1860
1817
|
this.resetSession();
|
|
1861
1818
|
}
|
|
1862
1819
|
}
|
|
1863
|
-
resetSession() {
|
|
1864
|
-
this.#session = {
|
|
1865
|
-
backupSatCode: null,
|
|
1866
|
-
httpClient: wrapper(axios.create({
|
|
1867
|
-
jar: new CookieJar(),
|
|
1868
|
-
})),
|
|
1869
|
-
isAuthenticated: false,
|
|
1870
|
-
isCleanState: true,
|
|
1871
|
-
networkId: null,
|
|
1872
|
-
portalVersion: null,
|
|
1873
|
-
};
|
|
1874
|
-
}
|
|
1875
1820
|
}
|
|
1876
1821
|
//# sourceMappingURL=api.js.map
|