UNPKG

homebridge-adt-pulse

Version:
2,005 lines 104 kB
import axios from 'axios';
import { wrapper } from 'axios-cookiejar-support';
import { JSDOM } from 'jsdom';
import _ from 'lodash';
import { isErrorLike, serializeError } from 'serialize-error';
import { CookieJar } from 'tough-cookie';
import { detectApiDoSubmitHandlers, detectApiGatewayInformation, detectApiOrbSecurityButtons, detectApiPanelInformation, detectApiPanelStatus, detectApiSensorsInformation, detectApiSensorsStatus, detectGlobalDebugParser, detectGlobalPortalVersion, } from './detect.js';
import { generateFakeDynatracePCHeaderValue, generateFakeReadyButtons, } from './fake.js';
import { paramNetworkId, paramSat, requestPathAccessSignIn, requestPathAccessSignInEXxPartnerAdt, requestPathAccessSignInNetworkIdXxPartnerAdt, requestPathAjaxSyncCheckServTXx, requestPathKeepAlive, requestPathMfaMfaSignInWorkflowChallenge, requestPathQuickControlArmDisarm, requestPathQuickControlServRunRraCommand, requestPathSummarySummary, requestPathSystemDeviceId1, requestPathSystemGateway, requestPathSystemSystem, textPanelEmergencyKeys, textPanelTypeModel, } from './regex.js';
import { debugLog, fetchErrorMessage, fetchMissingSatCode, fetchTableCells, findGatewayManufacturerModel, findNullKeys, findPanelManufacturer, generateHash, isPortalSyncCode, isSessionCleanState, parseArmDisarmMessage, parseDoSubmitHandlers, parseOrbSecurityButtons, parseOrbSensors, parseOrbTextSummary, parseSensorsTable, sleep, stackTracer, } from './utility.js';
export class ADTPulseAPI {
    #connection;
    #credentials;
    #internal;
    #session;
    constructor(config, internalConfig) {
        this.#connection = {
            subdomain: config.subdomain,
        };
        this.#credentials = {
            fingerprint: config.fingerprint,
            password: config.password,
            username: config.username,
        };
        this.#internal = {
            baseUrl: internalConfig.baseUrl ?? `https://${this.#connection.subdomain}.adtpulse.com`,
            debug: internalConfig.debug ?? false,
            logger: internalConfig.logger ?? null,
            reportedHashes: [],
            testMode: {
                enabled: internalConfig.testMode?.enabled ?? false,
                isSystemDisarmedBeforeTest: internalConfig.testMode?.isSystemDisarmedBeforeTest ?? false,
            },
            waitTimeAfterArm: 5000,
        };
        this.#session = {
            backupSatCode: null,
            httpClient: wrapper(axios.create({
                jar: new CookieJar(),
                validateStatus: () => true,
            })),
            isAuthenticated: false,
            isCleanState: true,
            networkId: null,
            portalVersion: null,
        };
        if (config.speed !== 1) {
            if (this.#internal.debug) {
                debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.constructor()', 'warn', `Plugin is now running under ${config.speed}x operational speed. You may see slower device updates`);
            }
            switch (config.speed) {
                case 0.75:
                    this.#internal.waitTimeAfterArm = 6000;
                    break;
                case 0.5:
                    this.#internal.waitTimeAfterArm = 7000;
                    break;
                case 0.25:
                    this.#internal.waitTimeAfterArm = 8000;
                    break;
                default:
                    break;
            }
        }
    }
    async login() {
        let errorObject;
        if (this.#internal.debug) {
            debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.login()', 'info', `Attempting to login to "${this.#internal.baseUrl}"`);
        }
        try {
            const sessions = {};
            if (this.isAuthenticated()) {
                if (this.#internal.debug) {
                    debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.login()', 'info', [
                        'Already logged in',
                        [
                            '(',
                            [
                                `backup sat code: ${this.#session.backupSatCode}`,
                                `network id: ${this.#session.networkId}`,
                                `portal version: ${this.#session.portalVersion}`,
                            ].join(', '),
                            ')',
                        ].join(''),
                    ].join(' '));
                }
                return {
                    action: 'LOGIN',
                    success: true,
                    info: {
                        backupSatCode: this.#session.backupSatCode,
                        networkId: this.#session.networkId,
                        portalVersion: this.#session.portalVersion,
                    },
                };
            }
            sessions.axiosIndex = await this.#session.httpClient.get(`${this.#internal.baseUrl}/`, this.getRequestConfig());
            if (sessions.axiosIndex.status >= 400) {
                if (this.#internal.debug) {
                    debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.login()', 'error', `The remote server responded with a HTTP ${sessions.axiosIndex.status} status code`);
                }
                return {
                    action: 'LOGIN',
                    success: false,
                    info: {
                        message: `The remote server responded with a HTTP ${sessions.axiosIndex.status} status code`,
                    },
                };
            }
            if (typeof sessions.axiosIndex?.request === 'undefined') {
                if (this.#internal.debug) {
                    debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.login()', 'error', 'The HTTP client responded without the "request" object');
                }
                return {
                    action: 'LOGIN',
                    success: false,
                    info: {
                        message: 'The HTTP client responded without the "request" object',
                    },
                };
            }
            const axiosIndexRequestPath = sessions.axiosIndex.request.path;
            const axiosIndexRequestPathValid = requestPathAccessSignIn.test(axiosIndexRequestPath);
            if (this.#internal.debug) {
                debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.login()', 'info', `Request path ➜ ${axiosIndexRequestPath}`);
                debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.login()', 'info', `Request path valid ➜ ${axiosIndexRequestPathValid}`);
            }
            if (!axiosIndexRequestPathValid) {
                if (this.#internal.debug) {
                    debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.login()', 'error', `"${axiosIndexRequestPath} is not the sign-in page`);
                }
                this.handleLoginFailure(axiosIndexRequestPath, sessions.axiosIndex);
                return {
                    action: 'LOGIN',
                    success: false,
                    info: {
                        message: `"${axiosIndexRequestPath} is not the sign-in page`,
                    },
                };
            }
            const loginForm = new URLSearchParams();
            loginForm.append('usernameForm', this.#credentials.username);
            loginForm.append('passwordForm', this.#credentials.password);
            loginForm.append('sun', 'yes');
            loginForm.append('networkid', '');
            loginForm.append('fingerprint', this.#credentials.fingerprint);
            this.#session.portalVersion = axiosIndexRequestPath.replace(requestPathAccessSignIn, '$2');
            await this.newInformationDispatcher('portal-version', { version: this.#session.portalVersion });
            sessions.axiosSignIn = await this.#session.httpClient.post(`${this.#internal.baseUrl}/myhome/${this.#session.portalVersion}/access/signin.jsp?e=ns&partner=adt`, loginForm, this.getRequestConfig({
                headers: {
                    'Cache-Control': 'max-age=0',
                    'Content-Type': 'application/x-www-form-urlencoded',
                    Origin: this.#internal.baseUrl,
                    Referer: `${this.#internal.baseUrl}/myhome/${this.#session.portalVersion}/access/signin.jsp?e=ns&partner=adt`,
                    'Sec-Fetch-Site': 'same-origin',
                },
            }));
            if (sessions.axiosIndex.status >= 400) {
                if (this.#internal.debug) {
                    debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.login()', 'error', `The remote server responded with a HTTP ${sessions.axiosIndex.status} status code`);
                }
                return {
                    action: 'LOGIN',
                    success: false,
                    info: {
                        message: `The remote server responded with a HTTP ${sessions.axiosIndex.status} status code`,
                    },
                };
            }
            if (typeof sessions.axiosSignIn?.request === 'undefined') {
                if (this.#internal.debug) {
                    debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.login()', 'error', 'The HTTP client responded without the "request" object');
                }
                return {
                    action: 'LOGIN',
                    success: false,
                    info: {
                        message: 'The HTTP client responded without the "request" object',
                    },
                };
            }
            const axiosSignInRequestPath = sessions.axiosSignIn.request.path;
            const axiosSignInRequestPathValid = requestPathSummarySummary.test(axiosSignInRequestPath);
            if (this.#internal.debug) {
                debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.login()', 'info', `Request path ➜ ${axiosSignInRequestPath}`);
                debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.login()', 'info', `Request path valid ➜ ${axiosSignInRequestPathValid}`);
            }
            if (!axiosSignInRequestPathValid) {
                if (this.#internal.debug) {
                    debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.login()', 'error', `"${axiosSignInRequestPath}" is not the summary page`);
                }
                this.handleLoginFailure(axiosSignInRequestPath, sessions.axiosSignIn);
                return {
                    action: 'LOGIN',
                    success: false,
                    info: {
                        message: `"${axiosSignInRequestPath}" is not the summary page`,
                    },
                };
            }
            if (typeof sessions.axiosSignIn.data !== 'string') {
                if (this.#internal.debug) {
                    debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.login()', 'error', 'The response body of the summary page is not of type "string"');
                }
                return {
                    action: 'LOGIN',
                    success: false,
                    info: {
                        message: 'The response body of the summary page is not of type "string"',
                    },
                };
            }
            const matchNetworkId = sessions.axiosSignIn.data.match(paramNetworkId);
            this.#session.networkId = (matchNetworkId !== null && matchNetworkId.length >= 2) ? matchNetworkId[1] : null;
            const matchSatCode = sessions.axiosSignIn.data.match(paramSat);
            this.#session.backupSatCode = (matchSatCode !== null && matchSatCode.length >= 2) ? matchSatCode[1] : null;
            if (this.#session.backupSatCode === null && this.#internal.debug) {
                debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.login()', 'warn', 'Unable to backup sat code, will try again when system becomes available');
            }
            this.#session.isAuthenticated = true;
            if (this.#internal.debug) {
                debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.login()', 'success', [
                    'Login successful',
                    [
                        '(',
                        [
                            `backup sat code: ${this.#session.backupSatCode}`,
                            `network id: ${this.#session.networkId}`,
                            `portal version: ${this.#session.portalVersion}`,
                        ].join(', '),
                        ')',
                    ].join(''),
                ].join(' '));
            }
            return {
                action: 'LOGIN',
                success: true,
                info: {
                    backupSatCode: this.#session.backupSatCode,
                    networkId: this.#session.networkId,
                    portalVersion: this.#session.portalVersion,
                },
            };
        }
        catch (error) {
            errorObject = (isErrorLike(error)) ? serializeError(error) : serializeError(new Error('Unknown error'));
        }
        if (this.#internal.debug) {
            debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.login()', 'error', 'Method encountered an error during execution');
            stackTracer('serialize-error', errorObject);
        }
        return {
            action: 'LOGIN',
            success: false,
            info: {
                error: errorObject,
            },
        };
    }
    async logout() {
        let errorObject;
        if (this.#internal.debug) {
            debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.logout()', 'info', `Attempting to logout of "${this.#internal.baseUrl}"`);
        }
        try {
            const sessions = {};
            if (!this.isAuthenticated()) {
                if (this.#internal.debug) {
                    debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.logout()', 'info', [
                        'Already logged out',
                        [
                            '(',
                            [
                                `backup sat code: ${this.#session.backupSatCode}`,
                                `network id: ${this.#session.networkId}`,
                                `portal version: ${this.#session.portalVersion}`,
                            ].join(', '),
                            ')',
                        ].join(''),
                    ].join(' '));
                }
                return {
                    action: 'LOGOUT',
                    success: true,
                    info: {
                        backupSatCode: this.#session.backupSatCode,
                        networkId: this.#session.networkId,
                        portalVersion: this.#session.portalVersion,
                    },
                };
            }
            sessions.axiosSignout = await this.#session.httpClient.get(`${this.#internal.baseUrl}/myhome/${this.#session.portalVersion}/access/signout.jsp?networkid=${this.#session.networkId}&partner=adt`, this.getRequestConfig({
                headers: {
                    Referer: `${this.#internal.baseUrl}/myhome/${this.#session.portalVersion}/summary/summary.jsp`,
                    'Sec-Fetch-Site': 'same-origin',
                },
            }));
            if (sessions.axiosSignout.status >= 400) {
                if (this.#internal.debug) {
                    debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.logout()', 'error', `The remote server responded with a HTTP ${sessions.axiosSignout.status} status code`);
                }
                return {
                    action: 'LOGOUT',
                    success: false,
                    info: {
                        message: `The remote server responded with a HTTP ${sessions.axiosSignout.status} status code`,
                    },
                };
            }
            if (typeof sessions.axiosSignout?.request === 'undefined') {
                if (this.#internal.debug) {
                    debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.logout()', 'error', 'The HTTP client responded without the "request" object');
                }
                return {
                    action: 'LOGOUT',
                    success: false,
                    info: {
                        message: 'The HTTP client responded without the "request" object',
                    },
                };
            }
            const axiosSignoutRequestPath = sessions.axiosSignout.request.path;
            const axiosSignoutRequestPathValid = requestPathAccessSignInNetworkIdXxPartnerAdt.test(axiosSignoutRequestPath);
            if (this.#internal.debug) {
                debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.logout()', 'info', `Request path ➜ ${axiosSignoutRequestPath}`);
                debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.logout()', 'info', `Request path valid ➜ ${axiosSignoutRequestPathValid}`);
            }
            if (!axiosSignoutRequestPathValid) {
                if (this.#internal.debug) {
                    debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.logout()', 'error', `"${axiosSignoutRequestPath}" is not the sign-in page with "networkid" and "partner=adt" parameters`);
                }
                this.handleLoginFailure(axiosSignoutRequestPath, sessions.axiosSignout);
                return {
                    action: 'LOGOUT',
                    success: false,
                    info: {
                        message: `"${axiosSignoutRequestPath}" is not the sign-in page with "networkid" and "partner=adt" parameters`,
                    },
                };
            }
            this.resetSession();
            if (this.#internal.debug) {
                debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.logout()', 'success', [
                    'Logout successful',
                    [
                        '(',
                        [
                            `backup sat code: ${this.#session.backupSatCode}`,
                            `network id: ${this.#session.networkId}`,
                            `portal version: ${this.#session.portalVersion}`,
                        ].join(', '),
                        ')',
                    ].join(''),
                ].join(' '));
            }
            return {
                action: 'LOGOUT',
                success: true,
                info: {
                    backupSatCode: this.#session.backupSatCode,
                    networkId: this.#session.networkId,
                    portalVersion: this.#session.portalVersion,
                },
            };
        }
        catch (error) {
            errorObject = (isErrorLike(error)) ? serializeError(error) : serializeError(new Error('Unknown error'));
        }
        if (this.#internal.debug) {
            debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.logout()', 'error', 'Method encountered an error during execution');
            stackTracer('serialize-error', errorObject);
        }
        return {
            action: 'LOGOUT',
            success: false,
            info: {
                error: errorObject,
            },
        };
    }
    async getGatewayInformation() {
        let errorObject;
        if (this.#internal.debug) {
            debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.getGatewayInformation()', 'info', `Attempting to retrieve gateway information from "${this.#internal.baseUrl}"`);
        }
        try {
            const sessions = {};
            sessions.axiosSystemGateway = await this.#session.httpClient.get(`${this.#internal.baseUrl}/myhome/${this.#session.portalVersion}/system/gateway.jsp`, this.getRequestConfig({
                headers: {
                    Referer: `${this.#internal.baseUrl}/myhome/${this.#session.portalVersion}/system/system.jsp`,
                    'Sec-Fetch-Site': 'same-origin',
                    'Sec-Fetch-User': undefined,
                },
            }));
            if (sessions.axiosSystemGateway.status >= 400) {
                if (this.#internal.debug) {
                    debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.getGatewayInformation()', 'error', `The remote server responded with a HTTP ${sessions.axiosSystemGateway.status} status code`);
                }
                return {
                    action: 'GET_GATEWAY_INFORMATION',
                    success: false,
                    info: {
                        message: `The remote server responded with a HTTP ${sessions.axiosSystemGateway.status} status code`,
                    },
                };
            }
            if (typeof sessions.axiosSystemGateway?.request === 'undefined') {
                if (this.#internal.debug) {
                    debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.getGatewayInformation()', 'error', 'The HTTP client responded without the "request" object');
                }
                return {
                    action: 'GET_GATEWAY_INFORMATION',
                    success: false,
                    info: {
                        message: 'The HTTP client responded without the "request" object',
                    },
                };
            }
            const axiosSystemGatewayRequestPath = sessions.axiosSystemGateway.request.path;
            const axiosSystemGatewayRequestPathValid = requestPathSystemGateway.test(axiosSystemGatewayRequestPath);
            if (this.#internal.debug) {
                debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.getGatewayInformation()', 'info', `Request path ➜ ${axiosSystemGatewayRequestPath}`);
                debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.getGatewayInformation()', 'info', `Request path valid ➜ ${axiosSystemGatewayRequestPathValid}`);
            }
            if (!axiosSystemGatewayRequestPathValid) {
                if (this.#internal.debug) {
                    debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.getGatewayInformation()', 'error', `"${axiosSystemGatewayRequestPath}" is not the system gateway page`);
                }
                this.handleLoginFailure(axiosSystemGatewayRequestPath, sessions.axiosSystemGateway);
                return {
                    action: 'GET_GATEWAY_INFORMATION',
                    success: false,
                    info: {
                        message: `"${axiosSystemGatewayRequestPath}" is not the system gateway page`,
                    },
                };
            }
            if (typeof sessions.axiosSystemGateway.data !== 'string') {
                if (this.#internal.debug) {
                    debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.getGatewayInformation()', 'error', 'The response body of the system gateway page is not of type "string"');
                }
                return {
                    action: 'GET_GATEWAY_INFORMATION',
                    success: false,
                    info: {
                        message: 'The response body of the system gateway page is not of type "string"',
                    },
                };
            }
            sessions.jsdomSystemGateway = new JSDOM(sessions.axiosSystemGateway.data, {
                url: sessions.axiosSystemGateway.config.url,
                referrer: sessions.axiosSystemGateway.config.headers.Referer,
                contentType: 'text/html',
                pretendToBeVisual: true,
            });
            const jsdomSystemGatewayTableCells = sessions.jsdomSystemGateway.window.document.querySelectorAll('td');
            const fetchedTableCells = fetchTableCells(jsdomSystemGatewayTableCells, [
                'Broadband Connection Status:',
                'Broadband LAN IP Address:',
                'Broadband LAN MAC:',
                'Cellular Connection Status:',
                'Cellular Signal Strength:',
                'Device LAN IP Address:',
                'Device LAN MAC:',
                'Firmware Version:',
                'Hardware Version:',
                'Last Update:',
                'Manufacturer:',
                'Model:',
                'Next Update:',
                'Primary Connection Type:',
                'Router LAN IP Address:',
                'Router WAN IP Address:',
                'Serial Number:',
                'Status:',
            ], 1, 1);
            const manufacturer = _.get(fetchedTableCells, ['Manufacturer:', 0], null);
            const model = _.get(fetchedTableCells, ['Model:', 0], null);
            const parsedManufacturer = findGatewayManufacturerModel('manufacturer', manufacturer, model);
            const parsedModel = findGatewayManufacturerModel('model', manufacturer, model);
            const gatewayInformation = {
                communication: {
                    broadbandConnectionStatus: _.get(fetchedTableCells, ['Broadband Connection Status:', 0], null),
                    cellularConnectionStatus: _.get(fetchedTableCells, ['Cellular Connection Status:', 0], null),
                    cellularSignalStrength: _.get(fetchedTableCells, ['Cellular Signal Strength:', 0], null),
                    primaryConnectionType: _.get(fetchedTableCells, ['Primary Connection Type:', 0], null),
                },
                manufacturer: parsedManufacturer,
                model: parsedModel,
                network: {
                    broadband: {
                        ip: _.get(fetchedTableCells, ['Broadband LAN IP Address:', 0], null),
                        mac: _.get(fetchedTableCells, ['Broadband LAN MAC:', 0], null),
                    },
                    device: {
                        ip: _.get(fetchedTableCells, ['Device LAN IP Address:', 0], null),
                        mac: _.get(fetchedTableCells, ['Device LAN MAC:', 0], null),
                    },
                    router: {
                        lanIp: _.get(fetchedTableCells, ['Router LAN IP Address:', 0], null),
                        wanIp: _.get(fetchedTableCells, ['Router WAN IP Address:', 0], null),
                    },
                },
                serialNumber: _.get(fetchedTableCells, ['Serial Number:', 0], null),
                status: _.get(fetchedTableCells, ['Status:', 0], null),
                update: {
                    last: _.get(fetchedTableCells, ['Last Update:', 0], null),
                    next: _.get(fetchedTableCells, ['Next Update:', 0], null),
                },
                versions: {
                    firmware: _.get(fetchedTableCells, ['Firmware Version:', 0], null),
                    hardware: _.get(fetchedTableCells, ['Hardware Version:', 0], null),
                },
            };
            await this.newInformationDispatcher('gateway-information', gatewayInformation);
            await this.newInformationDispatcher('debug-parser', {
                method: 'getGatewayInformation',
                response: fetchedTableCells,
                rawHtml: sessions.axiosSystemGateway.data,
            });
            if (this.#internal.debug) {
                debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.getGatewayInformation()', 'success', `Successfully retrieved gateway information from "${this.#internal.baseUrl}"`);
            }
            return {
                action: 'GET_GATEWAY_INFORMATION',
                success: true,
                info: gatewayInformation,
            };
        }
        catch (error) {
            errorObject = (isErrorLike(error)) ? serializeError(error) : serializeError(new Error('Unknown error'));
        }
        if (this.#internal.debug) {
            debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.getGatewayInformation()', 'error', 'Method encountered an error during execution');
            stackTracer('serialize-error', errorObject);
        }
        return {
            action: 'GET_GATEWAY_INFORMATION',
            success: false,
            info: {
                error: errorObject,
            },
        };
    }
    async getPanelInformation() {
        let errorObject;
        if (this.#internal.debug) {
            debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.getPanelInformation()', 'info', `Attempting to retrieve panel information from "${this.#internal.baseUrl}"`);
        }
        try {
            const sessions = {};
            sessions.axiosSystemDeviceId1 = await this.#session.httpClient.get(`${this.#internal.baseUrl}/myhome/${this.#session.portalVersion}/system/device.jsp?id=1`, this.getRequestConfig({
                headers: {
                    Referer: `${this.#internal.baseUrl}/myhome/${this.#session.portalVersion}/system/system.jsp`,
                    'Sec-Fetch-Site': 'same-origin',
                },
            }));
            if (sessions.axiosSystemDeviceId1.status >= 400) {
                if (this.#internal.debug) {
                    debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.getPanelInformation()', 'error', `The remote server responded with a HTTP ${sessions.axiosSystemDeviceId1.status} status code`);
                }
                return {
                    action: 'GET_PANEL_INFORMATION',
                    success: false,
                    info: {
                        message: `The remote server responded with a HTTP ${sessions.axiosSystemDeviceId1.status} status code`,
                    },
                };
            }
            if (typeof sessions.axiosSystemDeviceId1?.request === 'undefined') {
                if (this.#internal.debug) {
                    debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.getPanelInformation()', 'error', 'The HTTP client responded without the "request" object');
                }
                return {
                    action: 'GET_PANEL_INFORMATION',
                    success: false,
                    info: {
                        message: 'The HTTP client responded without the "request" object',
                    },
                };
            }
            const axiosSystemDeviceId1RequestPath = sessions.axiosSystemDeviceId1.request.path;
            const axiosSystemDeviceId1RequestPathValid = requestPathSystemDeviceId1.test(axiosSystemDeviceId1RequestPath);
            if (this.#internal.debug) {
                debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.getPanelInformation()', 'info', `Request path ➜ ${axiosSystemDeviceId1RequestPath}`);
                debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.getPanelInformation()', 'info', `Request path valid ➜ ${axiosSystemDeviceId1RequestPathValid}`);
            }
            if (!axiosSystemDeviceId1RequestPathValid) {
                if (this.#internal.debug) {
                    debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.getPanelInformation()', 'error', `"${axiosSystemDeviceId1RequestPath}" is not the system device id 1 page`);
                }
                this.handleLoginFailure(axiosSystemDeviceId1RequestPath, sessions.axiosSystemDeviceId1);
                return {
                    action: 'GET_PANEL_INFORMATION',
                    success: false,
                    info: {
                        message: `"${axiosSystemDeviceId1RequestPath}" is not the system device id 1 page`,
                    },
                };
            }
            if (typeof sessions.axiosSystemDeviceId1.data !== 'string') {
                if (this.#internal.debug) {
                    debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.getPanelInformation()', 'error', 'The response body of the system device id 1 page is not of type "string"');
                }
                return {
                    action: 'GET_PANEL_INFORMATION',
                    success: false,
                    info: {
                        message: 'The response body of the system device id 1 page is not of type "string"',
                    },
                };
            }
            sessions.jsdomSystemDeviceId1 = new JSDOM(sessions.axiosSystemDeviceId1.data, {
                url: sessions.axiosSystemDeviceId1.config.url,
                referrer: sessions.axiosSystemDeviceId1.config.headers.Referer,
                contentType: 'text/html',
                pretendToBeVisual: true,
            });
            const jsdomSystemDeviceId1TableCells = sessions.jsdomSystemDeviceId1.window.document.querySelectorAll('td');
            const fetchedTableCells = fetchTableCells(jsdomSystemDeviceId1TableCells, [
                'Emergency Keys:',
                'Manufacturer/Provider:',
                'Security Panel Master Code:',
                'Status:',
                'Type/Model:',
            ], 1, 1);
            const emergencyKeys = _.get(fetchedTableCells, ['Emergency Keys:', 0], null);
            const manufacturerProvider = _.get(fetchedTableCells, ['Manufacturer/Provider:', 0], null);
            const typeModel = _.get(fetchedTableCells, ['Type/Model:', 0], null);
            const parsedEmergencyKeys = (emergencyKeys !== null) ? emergencyKeys.match(textPanelEmergencyKeys) : null;
            const parsedManufacturer = findPanelManufacturer(manufacturerProvider, typeModel);
            const parsedType = (typeModel !== null && typeModel.includes(' - ')) ? typeModel.replace(textPanelTypeModel, '$1') : null;
            const parsedModel = (typeModel !== null) ? typeModel.replace(textPanelTypeModel, '$2') : null;
            const panelInformation = {
                emergencyKeys: parsedEmergencyKeys,
                manufacturer: parsedManufacturer,
                masterCode: _.get(fetchedTableCells, ['Security Panel Master Code:', 0], null),
                provider: 'ADT',
                type: parsedType,
                model: parsedModel,
                status: _.get(fetchedTableCells, ['Status:', 0], null),
            };
            await this.newInformationDispatcher('panel-information', panelInformation);
            await this.newInformationDispatcher('debug-parser', {
                method: 'getPanelInformation',
                response: fetchedTableCells,
                rawHtml: sessions.axiosSystemDeviceId1.data,
            });
            if (this.#internal.debug) {
                debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.getPanelInformation()', 'success', `Successfully retrieved panel information from "${this.#internal.baseUrl}"`);
            }
            return {
                action: 'GET_PANEL_INFORMATION',
                success: true,
                info: panelInformation,
            };
        }
        catch (error) {
            errorObject = (isErrorLike(error)) ? serializeError(error) : serializeError(new Error('Unknown error'));
        }
        if (this.#internal.debug) {
            debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.getPanelInformation()', 'error', 'Method encountered an error during execution');
            stackTracer('serialize-error', errorObject);
        }
        return {
            action: 'GET_PANEL_INFORMATION',
            success: false,
            info: {
                error: errorObject,
            },
        };
    }
    async getPanelStatus() {
        let errorObject;
        if (this.#internal.debug) {
            debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.getPanelStatus()', 'info', `Attempting to retrieve panel status from "${this.#internal.baseUrl}"`);
        }
        try {
            const sessions = {};
            sessions.axiosSummary = await this.#session.httpClient.get(`${this.#internal.baseUrl}/myhome/${this.#session.portalVersion}/summary/summary.jsp`, this.getRequestConfig({
                headers: {
                    Referer: `${this.#internal.baseUrl}/myhome/${this.#session.portalVersion}/summary/summary.jsp`,
                    'Sec-Fetch-Site': 'same-origin',
                },
            }));
            if (sessions.axiosSummary.status >= 400) {
                if (this.#internal.debug) {
                    debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.getPanelStatus()', 'error', `The remote server responded with a HTTP ${sessions.axiosSummary.status} status code`);
                }
                return {
                    action: 'GET_PANEL_STATUS',
                    success: false,
                    info: {
                        message: `The remote server responded with a HTTP ${sessions.axiosSummary.status} status code`,
                    },
                };
            }
            if (typeof sessions.axiosSummary?.request === 'undefined') {
                if (this.#internal.debug) {
                    debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.getPanelStatus()', 'error', 'The HTTP client responded without the "request" object');
                }
                return {
                    action: 'GET_PANEL_STATUS',
                    success: false,
                    info: {
                        message: 'The HTTP client responded without the "request" object',
                    },
                };
            }
            const axiosSummaryRequestPath = sessions.axiosSummary.request.path;
            const axiosSummaryRequestPathValid = requestPathSummarySummary.test(axiosSummaryRequestPath);
            if (this.#internal.debug) {
                debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.getPanelStatus()', 'info', `Request path ➜ ${axiosSummaryRequestPath}`);
                debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.getPanelStatus()', 'info', `Request path valid ➜ ${axiosSummaryRequestPathValid}`);
            }
            if (!axiosSummaryRequestPathValid) {
                if (this.#internal.debug) {
                    debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.getPanelStatus()', 'error', `"${axiosSummaryRequestPath}" is not the summary page`);
                }
                this.handleLoginFailure(axiosSummaryRequestPath, sessions.axiosSummary);
                return {
                    action: 'GET_PANEL_STATUS',
                    success: false,
                    info: {
                        message: `"${axiosSummaryRequestPath}" is not the summary page`,
                    },
                };
            }
            if (typeof sessions.axiosSummary.data !== 'string') {
                if (this.#internal.debug) {
                    debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.getPanelStatus()', 'error', 'The response body of the summary page is not of type "string"');
                }
                return {
                    action: 'GET_PANEL_STATUS',
                    success: false,
                    info: {
                        message: 'The response body of the summary page is not of type "string"',
                    },
                };
            }
            if (this.#session.backupSatCode === null) {
                const missingSatCode = fetchMissingSatCode(sessions.axiosSummary);
                if (missingSatCode !== null) {
                    if (this.#internal.debug) {
                        debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.getPanelStatus()', 'success', 'Backup sat code was successfully recovered from previous failed retrieval');
                    }
                    this.#session.backupSatCode = missingSatCode;
                }
            }
            sessions.jsdomSummary = new JSDOM(sessions.axiosSummary.data, {
                url: sessions.axiosSummary.config.url,
                referrer: sessions.axiosSummary.config.headers.Referer,
                contentType: 'text/html',
                pretendToBeVisual: true,
            });
            const jsdomSummaryOrbTextSummary = sessions.jsdomSummary.window.document.querySelector('#divOrbTextSummary');
            const parsedOrbTextSummary = parseOrbTextSummary(jsdomSummaryOrbTextSummary);
            await this.newInformationDispatcher('panel-status', parsedOrbTextSummary);
            await this.newInformationDispatcher('debug-parser', {
                method: 'getPanelStatus',
                response: parsedOrbTextSummary,
                rawHtml: sessions.axiosSummary.data,
            });
            if (this.#internal.debug) {
                debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.getPanelStatus()', 'success', `Successfully retrieved panel status from "${this.#internal.baseUrl}"`);
            }
            return {
                action: 'GET_PANEL_STATUS',
                success: true,
                info: parsedOrbTextSummary,
            };
        }
        catch (error) {
            errorObject = (isErrorLike(error)) ? serializeError(error) : serializeError(new Error('Unknown error'));
        }
        if (this.#internal.debug) {
            debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.getPanelStatus()', 'error', 'Method encountered an error during execution');
            stackTracer('serialize-error', errorObject);
        }
        return {
            action: 'GET_PANEL_STATUS',
            success: false,
            info: {
                error: errorObject,
            },
        };
    }
    async setPanelStatus(armFrom, armTo, isAlarmActive) {
        let errorObject;
        if (this.#internal.debug) {
            debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.setPanelStatus()', 'info', `Attempting to update panel status from "${armFrom}" to "${armTo}" at "${this.#internal.baseUrl}"`);
        }
        if (armFrom !== 'away'
            && armFrom !== 'night'
            && armFrom !== 'off'
            && armFrom !== 'stay') {
            return {
                action: 'SET_PANEL_STATUS',
                success: false,
                info: {
                    message: `"${armFrom}" is an invalid arm from state`,
                },
            };
        }
        if (armTo !== 'away'
            && armTo !== 'night'
            && armTo !== 'off'
            && armTo !== 'stay') {
            return {
                action: 'SET_PANEL_STATUS',
                success: false,
                info: {
                    message: `"${armTo}" is an invalid arm to state`,
                },
            };
        }
        if (typeof isAlarmActive !== 'boolean') {
            return {
                action: 'SET_PANEL_STATUS',
                success: false,
                info: {
                    message: 'You must specify if the system\'s alarm is currently ringing (true) or not (false)',
                },
            };
        }
        if ((armFrom === 'away'
            && armTo === 'away')
            || (armFrom === 'night'
                && armTo === 'night')
            || (armFrom === 'stay'
                && armTo === 'stay')
            || (armFrom === 'off'
                && armTo === 'off'
                && !isAlarmActive)) {
            if (this.#internal.debug) {
                debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.setPanelStatus()', 'info', `No need to change arm state from "${armFrom}" to "${armTo}" due to its equivalence`);
            }
            return {
                action: 'SET_PANEL_STATUS',
                success: true,
                info: {
                    forceArmRequired: false,
                },
            };
        }
        if (this.#internal.debug && isAlarmActive) {
            debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.setPanelStatus()', 'warn', `Alarm is currently ringing and arm state is being changed from "${armFrom}" to "${armTo}"`);
        }
        try {
            let isAlarmCurrentlyActive = isAlarmActive;
            const securityButtonsResponse = await this.getOrbSecurityButtons();
            if (!securityButtonsResponse.success) {
                if (this.#internal.debug) {
                    debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.setPanelStatus()', 'error', 'An error occurred while retrieving security buttons');
                }
                return {
                    action: 'SET_PANEL_STATUS',
                    success: false,
                    info: securityButtonsResponse.info,
                };
            }
            const securityButtons = securityButtonsResponse.info;
            let readyButtons = securityButtons.filter((securityButton) => !securityButton.buttonDisabled);
            if (readyButtons.length === 0 && this.#session.backupSatCode !== null) {
                readyButtons = generateFakeReadyButtons(securityButtons, this.#session.isCleanState, {
                    relativeUrl: 'quickcontrol/armDisarm.jsp',
                    href: 'rest/adt/ui/client/security/setArmState',
                    sat: this.#session.backupSatCode,
                });
                if (this.#internal.debug) {
                    debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.setPanelStatus()', 'warn', 'No security buttons were found. Replacing stuck orb security buttons with fake buttons');
                    stackTracer('fake-ready-buttons', {
                        before: securityButtons,
                        after: readyButtons,
                    });
                }
            }
            if (readyButtons.length === 0 && this.#session.backupSatCode === null) {
                if (this.#internal.debug) {
                    debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.setPanelStatus()', 'error', 'No security buttons were found and replacement failed because no backup sat code exists');
                }
                return {
                    action: 'SET_PANEL_STATUS',
                    success: false,
                    info: {
                        message: 'No security buttons were found and replacement failed because no backup sat code exists',
                    },
                };
            }
            if (readyButtons.length < 1) {
                if (this.#internal.debug) {
                    debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.setPanelStatus()', 'error', 'Security buttons are not found on the summary page');
                }
                return {
                    action: 'SET_PANEL_STATUS',
                    success: false,
                    info: {
                        message: 'Security buttons are not found on the summary page',
                    },
                };
            }
            if (this.#internal.testMode.enabled
                && !this.#internal.testMode.isSystemDisarmedBeforeTest) {
                if (!['off', 'disarmed'].includes(readyButtons[0].urlParams.armState)) {
                    if (this.#internal.debug) {
                        debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.setPanelStatus()', 'error', 'Test mode is active and system is not disarmed');
                    }
                    return {
                        action: 'SET_PANEL_STATUS',
                        success: false,
                        info: {
                            message: 'Test mode is active and system is not disarmed',
                        },
                    };
                }
                this.#internal.testMode.isSystemDisarmedBeforeTest = true;
            }
            while (isAlarmCurrentlyActive || !['off', 'disarmed'].includes(readyButtons[0].urlParams.armState)) {
                const armDisarmResponse = await this.armDisarmHandler(isAlarmCurrentlyActive, {
                    relativeUrl: readyButtons[0].relativeUrl,
                    href: readyButtons[0].urlParams.href,
                    armState: readyButtons[0].urlParams.armState,
                    arm: 'off',
                    sat: readyButtons[0].urlParams.sat,
                });
                if (!armDisarmResponse.success) {
                    if (this.#internal.debug) {
                        debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.setPanelStatus()', 'error', 'An error occurred in the arm disarm handler (while disarming)');
                    }
                    return {
                        action: 'SET_PANEL_STATUS',
                        success: false,
                        info: armDisarmResponse.info,
                    };
                }
                if (armDisarmResponse.info.readyButtons.length < 1) {
                    if (this.#internal.debug) {
                        debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.setPanelStatus()', 'error', 'Arm disarm handler failed to find new security buttons');
                    }
                    return {
                        action: 'SET_PANEL_STATUS',
                        success: false,
                        info: {
                            message: 'Arm disarm handler failed to find new security buttons',
                        },
                    };
                }
                readyButtons = armDisarmResponse.info.readyButtons;
                if (isAlarmCurrentlyActive) {
                    isAlarmCurrentlyActive = false;
                }
            }
            let forceArmRequired = false;
            if (armTo !== 'off') {
                const armDisarmResponse = await this.armDisarmHandler(false, {
                    relativeUrl: readyButtons[0].relativeUrl,
                    href: readyButtons[0].urlParams.href,
                    armState: readyButtons[0].urlParams.armState,
                    arm: armTo,
                    sat: readyButtons[0].urlParams.sat,
                });
                if (!armDisarmResponse.success) {
                    if (this.#internal.debug) {
                        debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.setPanelStatus()', 'error', 'An error occurred in the arm disarm handler (while arming)');
                    }
                    return {
                        action: 'SET_PANEL_STATUS',
                        success: false,
                        info: armDisarmResponse.info,
                    };
                }
                forceArmRequired = armDisarmResponse.info.forceArmRequired;
            }
            if (this.#internal.debug) {
                debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.setPanelStatus()', 'success', `Successfully updated panel status from "${armFrom}" to "${armTo}" at "${this.#internal.baseUrl}"`);
            }
            return {
                action: 'SET_PANEL_STATUS',
                success: true,
                info: {
                    forceArmRequired,
                },
            };
        }
        catch (error) {
            errorObject = (isErrorLike(error)) ? serializeError(error) : serializeError(new Error('Unknown error'));
        }
        if (this.#internal.debug) {
            debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.setPanelStatus()', 'error', 'Method encountered an error during execution');
            stackTracer('serialize-error', errorObject);
        }
        return {
            action: 'SET_PANEL_STATUS',
            success: false,
            info: {
                error: errorObject,
            },
        };
    }
    async getSensorsInformation() {
        let errorObject;
        if (this.#internal.debug) {
            debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.getSensorsInformation()', 'info', `Attempting to retrieve sensors information from "${this.#internal.baseUrl}"`);
        }
        try {
            const sessions = {};
            sessions.axiosSystem = await this.#session.httpClient.get(`${this.#internal.baseUrl}/myhome/${this.#session.portalVersion}/system/system.jsp`, this.getRequestConfig({
                headers: {
                    Referer: `${this.#internal.baseUrl}/myhome/${this.#session.portalVersion}/summary/summary.jsp`,
                    'Sec-Fetch-Site': 'same-origin',
                },
            }));
            if (sessions.axiosSystem.status >= 400) {
                if (this.#internal.debug) {
                    debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.getSensorsInformation()', 'error', `The remote server responded with a HTTP ${sessions.axiosSystem.status} status code`);
                }
                return {
                    action: 'GET_SENSORS_INFORMATION',
                    success: false,
                    info: {
                        message: `The remote server responded with a HTTP ${sessions.axiosSystem.status} status code`,
                    },
                };
            }
            if (typeof sessions.axiosSystem?.request === 'undefined') {
                if (this.#internal.debug) {
                    debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.getSensorsInformation()', 'error', 'The HTTP client responded without the "request" object');
                }
                return {
                    action: 'GET_SENSORS_INFORMATION',
                    success: false,
                    info: {
                        message: 'The HTTP client responded without the "request" object',
                    },
                };
            }
            const axiosSystemRequestPath = sessions.axiosSystem.request.path;
            const axiosSystemRequestPathValid = requestPathSystemSystem.test(axiosSystemRequestPath);
            if (this.#internal.debug) {
                debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.getSensorsInformation()', 'info', `Request path ➜ ${axiosSystemRequestPath}`);
                debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.getSensorsInformation()', 'info', `Request path valid ➜ ${axiosSystemRequestPathValid}`);
            }
            if (!axiosSystemRequestPathValid) {
                if (this.#internal.debug) {
                    debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.getSensorsInformation()', 'error', `"${axiosSystemRequestPath}" is not the system page`);
                }
                this.handleLoginFailure(axiosSystemRequestPath, sessions.axiosSystem);
                return {
                    action: 'GET_SENSORS_INFORMATION',
                    success: false,
                    info: {
                        message: `"${axiosSystemRequestPath}" is not the system page`,
                    },
                };
            }
            if (typeof sessions.axiosSystem.data !== 'string') {
                if (this.#internal.debug) {
                    debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.getSensorsInformation()', 'error', 'The response body of the system page is not of type "string"');
                }
                return {
                    action: 'GET_SENSORS_INFORMATION',
                    success: false,
                    info: {
                        message: 'The response body of the system page is not of type "string"',
                    },
                };
            }
            sessions.jsdomSystem = new JSDOM(sessions.axiosSystem.data, {
                url: sessions.axiosSystem.config.url,
                referrer: sessions.axiosSystem.config.headers.Referer,
                contentType: 'text/html',
                pretendToBeVisual: true,
            });
            const jsdomSystemSensorsTable = sessions.jsdomSystem.window.document.querySelectorAll('#systemContentList tr[class^=\'p_row\'] tr.p_listRow');
            const parsedSensorsInformationTable = parseSensorsTable('sensors-information', jsdomSystemSensorsTable);
            await this.newInformationDispatcher('sensors-information', parsedSensorsInformationTable);
            await this.newInformationDispatcher('debug-parser', {
                method: 'getSensorsInformation',
                response: parsedSensorsInformationTable,
                rawHtml: sessions.axiosSystem.data,
            });
            if (this.#internal.debug) {
                debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.getSensorsInformation()', 'success', `Successfully retrieved sensors information from "${this.#internal.baseUrl}"`);
            }
            return {
                action: 'GET_SENSORS_INFORMATION',
                success: true,
                info: {
                    sensors: parsedSensorsInformationTable,
                },
            };
        }
        catch (error) {
            errorObject = (isErrorLike(error)) ? serializeError(error) : serializeError(new Error('Unknown error'));
        }
        if (this.#internal.debug) {
            debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.getSensorsInformation()', 'error', 'Method encountered an error during execution');
            stackTracer('serialize-error', errorObject);
        }
        return {
            action: 'GET_SENSORS_INFORMATION',
            success: false,
            info: {
                error: errorObject,
            },
        };
    }
    async getSensorsStatus() {
        let errorObject;
        if (this.#internal.debug) {
            debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.getSensorsStatus()', 'info', `Attempting to retrieve sensors status from "${this.#internal.baseUrl}"`);
        }
        try {
            const sessions = {};
            sessions.axiosSummary = await this.#session.httpClient.get(`${this.#internal.baseUrl}/myhome/${this.#session.portalVersion}/summary/summary.jsp`, this.getRequestConfig({
                headers: {
                    Referer: `${this.#internal.baseUrl}/myhome/${this.#session.portalVersion}/summary/summary.jsp`,
                    'Sec-Fetch-Site': 'same-origin',
                },
            }));
            if (sessions.axiosSummary.status >= 400) {
                if (this.#internal.debug) {
                    debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.getSensorsStatus()', 'error', `The remote server responded with a HTTP ${sessions.axiosSummary.status} status code`);
                }
                return {
                    action: 'GET_SENSORS_STATUS',
                    success: false,
                    info: {
                        message: `The remote server responded with a HTTP ${sessions.axiosSummary.status} status code`,
                    },
                };
            }
            if (typeof sessions.axiosSummary?.request === 'undefined') {
                if (this.#internal.debug) {
                    debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.getSensorsStatus()', 'error', 'The HTTP client responded without the "request" object');
                }
                return {
                    action: 'GET_SENSORS_STATUS',
                    success: false,
                    info: {
                        message: 'The HTTP client responded without the "request" object',
                    },
                };
            }
            const axiosSummaryRequestPath = sessions.axiosSummary.request.path;
            const axiosSummaryRequestPathValid = requestPathSummarySummary.test(axiosSummaryRequestPath);
            if (this.#internal.debug) {
                debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.getSensorsStatus()', 'info', `Request path ➜ ${axiosSummaryRequestPath}`);
                debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.getSensorsStatus()', 'info', `Request path valid ➜ ${axiosSummaryRequestPathValid}`);
            }
            if (!axiosSummaryRequestPathValid) {
                if (this.#internal.debug) {
                    debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.getSensorsStatus()', 'error', `"${axiosSummaryRequestPath}" is not the summary page`);
                }
                this.handleLoginFailure(axiosSummaryRequestPath, sessions.axiosSummary);
                return {
                    action: 'GET_SENSORS_STATUS',
                    success: false,
                    info: {
                        message: `"${axiosSummaryRequestPath}" is not the summary page`,
                    },
                };
            }
            if (typeof sessions.axiosSummary.data !== 'string') {
                if (this.#internal.debug) {
                    debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.getSensorsStatus()', 'error', 'The response body of the summary page is not of type "string"');
                }
                return {
                    action: 'GET_SENSORS_STATUS',
                    success: false,
                    info: {
                        message: 'The response body of the summary page is not of type "string"',
                    },
                };
            }
            if (this.#session.backupSatCode === null) {
                const missingSatCode = fetchMissingSatCode(sessions.axiosSummary);
                if (missingSatCode !== null) {
                    if (this.#internal.debug) {
                        debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.getSensorsStatus()', 'success', 'Backup sat code was successfully recovered from previous failed retrieval');
                    }
                    this.#session.backupSatCode = missingSatCode;
                }
            }
            sessions.jsdomSummary = new JSDOM(sessions.axiosSummary.data, {
                url: sessions.axiosSummary.config.url,
                referrer: sessions.axiosSummary.config.headers.Referer,
                contentType: 'text/html',
                pretendToBeVisual: true,
            });
            const jsdomSummaryOrbSensors = sessions.jsdomSummary.window.document.querySelectorAll('#orbSensorsList tr.p_listRow');
            const parsedOrbSensors = parseOrbSensors(jsdomSummaryOrbSensors);
            await this.newInformationDispatcher('sensors-status', parsedOrbSensors);
            await this.newInformationDispatcher('debug-parser', {
                method: 'getSensorsStatus',
                response: parsedOrbSensors,
                rawHtml: sessions.axiosSummary.data,
            });
            if (this.#internal.debug) {
                debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.getSensorsStatus()', 'success', `Successfully retrieved sensors status from "${this.#internal.baseUrl}"`);
            }
            return {
                action: 'GET_SENSORS_STATUS',
                success: true,
                info: {
                    sensors: parsedOrbSensors,
                },
            };
        }
        catch (error) {
            errorObject = (isErrorLike(error)) ? serializeError(error) : serializeError(new Error('Unknown error'));
        }
        if (this.#internal.debug) {
            debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.getSensorsStatus()', 'error', 'Method encountered an error during execution');
            stackTracer('serialize-error', errorObject);
        }
        return {
            action: 'GET_SENSORS_STATUS',
            success: false,
            info: {
                error: errorObject,
            },
        };
    }
    async getOrbSecurityButtons() {
        let errorObject;
        if (this.#internal.debug) {
            debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.getOrbSecurityButtons()', 'info', `Attempting to retrieve orb security buttons from "${this.#internal.baseUrl}"`);
        }
        try {
            const sessions = {};
            sessions.axiosSummary = await this.#session.httpClient.get(`${this.#internal.baseUrl}/myhome/${this.#session.portalVersion}/summary/summary.jsp`, this.getRequestConfig({
                headers: {
                    Referer: `${this.#internal.baseUrl}/myhome/${this.#session.portalVersion}/summary/summary.jsp`,
                    'Sec-Fetch-Site': 'same-origin',
                },
            }));
            if (sessions.axiosSummary.status >= 400) {
                if (this.#internal.debug) {
                    debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.getOrbSecurityButtons()', 'error', `The remote server responded with a HTTP ${sessions.axiosSummary.status} status code`);
                }
                return {
                    action: 'GET_ORB_SECURITY_BUTTONS',
                    success: false,
                    info: {
                        message: `The remote server responded with a HTTP ${sessions.axiosSummary.status} status code`,
                    },
                };
            }
            if (typeof sessions.axiosSummary?.request === 'undefined') {
                if (this.#internal.debug) {
                    debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.getOrbSecurityButtons()', 'error', 'The HTTP client responded without the "request" object');
                }
                return {
                    action: 'GET_ORB_SECURITY_BUTTONS',
                    success: false,
                    info: {
                        message: 'The HTTP client responded without the "request" object',
                    },
                };
            }
            const axiosSummaryRequestPath = sessions.axiosSummary.request.path;
            const axiosSummaryRequestPathValid = requestPathSummarySummary.test(axiosSummaryRequestPath);
            if (this.#internal.debug) {
                debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.getOrbSecurityButtons()', 'info', `Request path ➜ ${axiosSummaryRequestPath}`);
                debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.getOrbSecurityButtons()', 'info', `Request path valid ➜ ${axiosSummaryRequestPathValid}`);
            }
            if (!axiosSummaryRequestPathValid) {
                if (this.#internal.debug) {
                    debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.getOrbSecurityButtons()', 'error', `"${axiosSummaryRequestPath}" is not the summary page`);
                }
                this.handleLoginFailure(axiosSummaryRequestPath, sessions.axiosSummary);
                return {
                    action: 'GET_ORB_SECURITY_BUTTONS',
                    success: false,
                    info: {
                        message: `"${axiosSummaryRequestPath}" is not the summary page`,
                    },
                };
            }
            if (typeof sessions.axiosSummary.data !== 'string') {
                if (this.#internal.debug) {
                    debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.getOrbSecurityButtons()', 'error', 'The response body of the summary page is not of type "string"');
                }
                return {
                    action: 'GET_ORB_SECURITY_BUTTONS',
                    success: false,
                    info: {
                        message: 'The response body of the summary page is not of type "string"',
                    },
                };
            }
            if (this.#session.backupSatCode === null) {
                const missingSatCode = fetchMissingSatCode(sessions.axiosSummary);
                if (missingSatCode !== null) {
                    if (this.#internal.debug) {
                        debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.getOrbSecurityButtons()', 'success', 'Backup sat code was successfully recovered from previous failed retrieval');
                    }
                    this.#session.backupSatCode = missingSatCode;
                }
            }
            sessions.jsdomSummary = new JSDOM(sessions.axiosSummary.data, {
                url: sessions.axiosSummary.config.url,
                referrer: sessions.axiosSummary.config.headers.Referer,
                contentType: 'text/html',
                pretendToBeVisual: true,
            });
            const jsdomSummaryOrbSecurityButtons = sessions.jsdomSummary.window.document.querySelectorAll('#divOrbSecurityButtons input');
            const parsedOrbSecurityButtons = parseOrbSecurityButtons(jsdomSummaryOrbSecurityButtons);
            await this.newInformationDispatcher('orb-security-buttons', parsedOrbSecurityButtons);
            await this.newInformationDispatcher('debug-parser', {
                method: 'getOrbSecurityButtons',
                response: parsedOrbSecurityButtons,
                rawHtml: sessions.axiosSummary.data,
            });
            this.#session.isCleanState = isSessionCleanState(parsedOrbSecurityButtons);
            if (this.#internal.debug) {
                debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.getOrbSecurityButtons()', 'success', `Successfully retrieved orb security buttons from "${this.#internal.baseUrl}"`);
            }
            return {
                action: 'GET_ORB_SECURITY_BUTTONS',
                success: true,
                info: parsedOrbSecurityButtons,
            };
        }
        catch (error) {
            errorObject = (isErrorLike(error)) ? serializeError(error) : serializeError(new Error('Unknown error'));
        }
        if (this.#internal.debug) {
            debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.getOrbSecurityButtons()', 'error', 'Method encountered an error during execution');
            stackTracer('serialize-error', errorObject);
        }
        return {
            action: 'GET_ORB_SECURITY_BUTTONS',
            success: false,
            info: {
                error: errorObject,
            },
        };
    }
    async performSyncCheck() {
        let errorObject;
        if (this.#internal.debug) {
            debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.performSyncCheck()', 'info', `Attempting to perform a sync check from "${this.#internal.baseUrl}"`);
        }
        try {
            const sessions = {};
            sessions.axiosSyncCheck = await this.#session.httpClient.get(`${this.#internal.baseUrl}/myhome/${this.#session.portalVersion}/Ajax/SyncCheckServ?t=${Date.now()}`, this.getRequestConfig({
                headers: {
                    Accept: '*/*',
                    Referer: `${this.#internal.baseUrl}/myhome/${this.#session.portalVersion}/summary/summary.jsp`,
                    'Sec-Fetch-Dest': 'empty',
                    'Sec-Fetch-Mode': 'cors',
                    'Sec-Fetch-Site': 'same-origin',
                    'Sec-Fetch-User': undefined,
                    'Upgrade-Insecure-Requests': undefined,
                },
            }));
            if (sessions.axiosSyncCheck.status >= 400) {
                if (this.#internal.debug) {
                    debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.performSyncCheck()', 'error', `The remote server responded with a HTTP ${sessions.axiosSyncCheck.status} status code`);
                }
                return {
                    action: 'PERFORM_SYNC_CHECK',
                    success: false,
                    info: {
                        message: `The remote server responded with a HTTP ${sessions.axiosSyncCheck.status} status code`,
                    },
                };
            }
            if (typeof sessions.axiosSyncCheck?.request === 'undefined') {
                if (this.#internal.debug) {
                    debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.performSyncCheck()', 'error', 'The HTTP client responded without the "request" object');
                }
                return {
                    action: 'PERFORM_SYNC_CHECK',
                    success: false,
                    info: {
                        message: 'The HTTP client responded without the "request" object',
                    },
                };
            }
            const axiosSyncCheckRequestPath = sessions.axiosSyncCheck.request.path;
            const axiosSyncCheckRequestPathValid = requestPathAjaxSyncCheckServTXx.test(axiosSyncCheckRequestPath);
            if (this.#internal.debug) {
                debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.performSyncCheck()', 'info', `Request path ➜ ${axiosSyncCheckRequestPath}`);
                debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.performSyncCheck()', 'info', `Request path valid ➜ ${axiosSyncCheckRequestPathValid}`);
            }
            if (!axiosSyncCheckRequestPathValid) {
                if (this.#internal.debug) {
                    debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.performSyncCheck()', 'error', `"${axiosSyncCheckRequestPath}" is not the sync check page`);
                }
                this.handleLoginFailure(axiosSyncCheckRequestPath, sessions.axiosSyncCheck);
                return {
                    action: 'PERFORM_SYNC_CHECK',
                    success: false,
                    info: {
                        message: `"${axiosSyncCheckRequestPath}" is not the sync check page`,
                    },
                };
            }
            if (typeof sessions.axiosSyncCheck.data !== 'string') {
                if (this.#internal.debug) {
                    debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.performSyncCheck()', 'error', 'The response body of the sync check page is not of type "string"');
                }
                return {
                    action: 'PERFORM_SYNC_CHECK',
                    success: false,
                    info: {
                        message: 'The response body of the sync check page is not of type "string"',
                    },
                };
            }
            if (!isPortalSyncCode(sessions.axiosSyncCheck.data)) {
                if (this.#internal.debug) {
                    debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.performSyncCheck()', 'error', 'The sync code structure is invalid');
                }
                return {
                    action: 'PERFORM_SYNC_CHECK',
                    success: false,
                    info: {
                        message: 'The sync code structure is invalid',
                    },
                };
            }
            if (this.#internal.debug) {
                debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.performSyncCheck()', 'success', `Successfully performed a sync check from "${this.#internal.baseUrl}"`);
            }
            return {
                action: 'PERFORM_SYNC_CHECK',
                success: true,
                info: {
                    syncCode: sessions.axiosSyncCheck.data,
                },
            };
        }
        catch (error) {
            errorObject = (isErrorLike(error)) ? serializeError(error) : serializeError(new Error('Unknown error'));
        }
        if (this.#internal.debug) {
            debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.performSyncCheck()', 'error', 'Method encountered an error during execution');
            stackTracer('serialize-error', errorObject);
        }
        return {
            action: 'PERFORM_SYNC_CHECK',
            success: false,
            info: {
                error: errorObject,
            },
        };
    }
    async performKeepAlive() {
        let errorObject;
        if (this.#internal.debug) {
            debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.performKeepAlive()', 'info', `Attempting to perform a keep alive from "${this.#internal.baseUrl}"`);
        }
        try {
            const sessions = {};
            sessions.axiosKeepAlive = await this.#session.httpClient.post(`${this.#internal.baseUrl}/myhome/${this.#session.portalVersion}/KeepAlive`, '', this.getRequestConfig({
                headers: {
                    Accept: '*/*',
                    'Content-type': 'application/x-www-form-urlencoded',
                    Origin: this.#internal.baseUrl,
                    Referer: `${this.#internal.baseUrl}/myhome/${this.#session.portalVersion}/summary/summary.jsp`,
                    'Sec-Fetch-Dest': 'empty',
                    'Sec-Fetch-Mode': 'cors',
                    'Sec-Fetch-Site': 'same-origin',
                    'Sec-Fetch-User': undefined,
                    'Upgrade-Insecure-Requests': undefined,
                    'x-dtpc': generateFakeDynatracePCHeaderValue('keep-alive'),
                },
            }));
            if (sessions.axiosKeepAlive.status >= 400) {
                if (this.#internal.debug) {
                    debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.performKeepAlive()', 'error', `The remote server responded with a HTTP ${sessions.axiosKeepAlive.status} status code`);
                }
                return {
                    action: 'PERFORM_KEEP_ALIVE',
                    success: false,
                    info: {
                        message: `The remote server responded with a HTTP ${sessions.axiosKeepAlive.status} status code`,
                    },
                };
            }
            if (typeof sessions.axiosKeepAlive?.request === 'undefined') {
                if (this.#internal.debug) {
                    debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.performKeepAlive()', 'error', 'The HTTP client responded without the "request" object');
                }
                return {
                    action: 'PERFORM_KEEP_ALIVE',
                    success: false,
                    info: {
                        message: 'The HTTP client responded without the "request" object',
                    },
                };
            }
            const axiosKeepAliveRequestPath = sessions.axiosKeepAlive.request.path;
            const axiosKeepAliveRequestPathValid = requestPathKeepAlive.test(axiosKeepAliveRequestPath);
            if (this.#internal.debug) {
                debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.performKeepAlive()', 'info', `Request path ➜ ${axiosKeepAliveRequestPath}`);
                debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.performKeepAlive()', 'info', `Request path valid ➜ ${axiosKeepAliveRequestPathValid}`);
            }
            if (!axiosKeepAliveRequestPathValid) {
                if (this.#internal.debug) {
                    debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.performKeepAlive()', 'error', `"${axiosKeepAliveRequestPath}" is not the keep alive page`);
                }
                this.handleLoginFailure(axiosKeepAliveRequestPath, sessions.axiosKeepAlive);
                return {
                    action: 'PERFORM_KEEP_ALIVE',
                    success: false,
                    info: {
                        message: `"${axiosKeepAliveRequestPath}" is not the keep alive page`,
                    },
                };
            }
            if (this.#internal.debug) {
                debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.performKeepAlive()', 'success', `Successfully performed a keep alive from "${this.#internal.baseUrl}"`);
            }
            return {
                action: 'PERFORM_KEEP_ALIVE',
                success: true,
                info: null,
            };
        }
        catch (error) {
            errorObject = (isErrorLike(error)) ? serializeError(error) : serializeError(new Error('Unknown error'));
        }
        if (this.#internal.debug) {
            debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.performKeepAlive()', 'error', 'Method encountered an error during execution');
            stackTracer('serialize-error', errorObject);
        }
        return {
            action: 'PERFORM_KEEP_ALIVE',
            success: false,
            info: {
                error: errorObject,
            },
        };
    }
    isAuthenticated() {
        return this.#session.isAuthenticated;
    }
    resetSession() {
        this.#session = {
            backupSatCode: null,
            httpClient: wrapper(axios.create({
                jar: new CookieJar(),
                validateStatus: () => true,
            })),
            isAuthenticated: false,
            isCleanState: true,
            networkId: null,
            portalVersion: null,
        };
    }
    async armDisarmHandler(isAlarmActive, options) {
        let errorObject;
        if (this.#internal.debug) {
            debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.armDisarmHandler()', 'info', `Attempting to update arm state from "${options.armState}" to "${options.arm}" on "${this.#internal.baseUrl}"`);
        }
        if ((options.armState === 'away'
            && options.arm === 'away')
            || ((options.armState === 'night'
                || options.armState === 'night+stay')
                && options.arm === 'night')
            || (options.armState === 'stay'
                && options.arm === 'stay')
            || ((options.armState === 'disarmed'
                || options.armState === 'off')
                && options.arm === 'off'
                && !isAlarmActive)) {
            if (this.#internal.debug) {
                debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.armDisarmHandler()', 'info', `No need to change arm state from "${options.armState}" to "${options.arm}" due to its equivalence`);
            }
            return {
                action: 'ARM_DISARM_HANDLER',
                success: true,
                info: {
                    forceArmRequired: false,
                    readyButtons: [],
                },
            };
        }
        try {
            const sessions = {};
            const armDisarmForm = new URLSearchParams();
            armDisarmForm.append('href', options.href);
            armDisarmForm.append('armstate', options.armState);
            armDisarmForm.append('arm', options.arm);
            armDisarmForm.append('sat', options.sat);
            sessions.axiosSetArmMode = await this.#session.httpClient.post(`${this.#internal.baseUrl}/myhome/${this.#session.portalVersion}/${options.relativeUrl}`, armDisarmForm, this.getRequestConfig({
                headers: {
                    'Cache-Control': 'max-age=0',
                    'Content-Type': 'application/x-www-form-urlencoded',
                    Origin: this.#internal.baseUrl,
                    Referer: `${this.#internal.baseUrl}/myhome/${this.#session.portalVersion}/summary/summary.jsp`,
                    'Sec-Fetch-Dest': 'iframe',
                    'Sec-Fetch-Mode': 'navigate',
                    'Sec-Fetch-Site': 'same-origin',
                    'Sec-Fetch-User': undefined,
                },
            }));
            if (sessions.axiosSetArmMode.status >= 400) {
                if (this.#internal.debug) {
                    debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.armDisarmHandler()', 'error', `The remote server responded with a HTTP ${sessions.axiosSetArmMode.status} status code`);
                }
                return {
                    action: 'ARM_DISARM_HANDLER',
                    success: false,
                    info: {
                        message: `The remote server responded with a HTTP ${sessions.axiosSetArmMode.status} status code`,
                    },
                };
            }
            if (typeof sessions.axiosSetArmMode?.request === 'undefined') {
                if (this.#internal.debug) {
                    debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.armDisarmHandler()', 'error', 'The HTTP client responded without the "request" object');
                }
                return {
                    action: 'ARM_DISARM_HANDLER',
                    success: false,
                    info: {
                        message: 'The HTTP client responded without the "request" object',
                    },
                };
            }
            const axiosSetArmModeRequestPath = sessions.axiosSetArmMode.request.path;
            const axiosSetArmModeRequestPathValid = requestPathQuickControlArmDisarm.test(axiosSetArmModeRequestPath);
            if (this.#internal.debug) {
                debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.armDisarmHandler()', 'info', `Request path ➜ ${axiosSetArmModeRequestPath}`);
                debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.armDisarmHandler()', 'info', `Request path valid ➜ ${axiosSetArmModeRequestPathValid}`);
            }
            if (!axiosSetArmModeRequestPathValid) {
                if (this.#internal.debug) {
                    debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.armDisarmHandler()', 'error', `"${axiosSetArmModeRequestPath}" is not the arm disarm page`);
                }
                this.handleLoginFailure(axiosSetArmModeRequestPath, sessions.axiosSetArmMode);
                return {
                    action: 'ARM_DISARM_HANDLER',
                    success: false,
                    info: {
                        message: `"${axiosSetArmModeRequestPath}" is not the arm disarm page`,
                    },
                };
            }
            let forceArmRequired = false;
            if (options.arm !== 'off') {
                const forceArmResponse = await this.forceArmHandler(sessions.axiosSetArmMode, options.relativeUrl);
                if (!forceArmResponse.success) {
                    if (this.#internal.debug) {
                        debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.armDisarmHandler()', 'error', 'An error occurred in the force arm handler');
                    }
                    return {
                        action: 'ARM_DISARM_HANDLER',
                        success: false,
                        info: forceArmResponse.info,
                    };
                }
                forceArmRequired = forceArmResponse.info.forceArmRequired;
            }
            await sleep(this.#internal.waitTimeAfterArm);
            const securityButtonsResponse = await this.getOrbSecurityButtons();
            if (!securityButtonsResponse.success) {
                if (this.#internal.debug) {
                    debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.armDisarmHandler()', 'error', 'An error occurred while retrieving security buttons');
                }
                return {
                    action: 'ARM_DISARM_HANDLER',
                    success: false,
                    info: securityButtonsResponse.info,
                };
            }
            const securityButtons = securityButtonsResponse.info;
            let readyButtons = securityButtons.filter((securityButton) => !securityButton.buttonDisabled);
            if (readyButtons.length === 0) {
                readyButtons = generateFakeReadyButtons(securityButtons, this.#session.isCleanState, {
                    relativeUrl: options.relativeUrl,
                    href: options.href,
                    sat: options.sat,
                });
                if (this.#internal.debug) {
                    debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.armDisarmHandler()', 'warn', 'No security buttons were found. Replacing stuck orb security buttons with fake buttons');
                    stackTracer('fake-ready-buttons', {
                        before: securityButtons,
                        after: readyButtons,
                    });
                }
            }
            if (this.#internal.debug) {
                debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.armDisarmHandler()', 'success', `Successfully updated arm state from "${options.armState}" to "${options.arm}" on "${this.#internal.baseUrl}"`);
            }
            return {
                action: 'ARM_DISARM_HANDLER',
                success: true,
                info: {
                    forceArmRequired,
                    readyButtons,
                },
            };
        }
        catch (error) {
            errorObject = (isErrorLike(error)) ? serializeError(error) : serializeError(new Error('Unknown error'));
        }
        if (this.#internal.debug) {
            debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.armDisarmHandler()', 'error', 'Method encountered an error during execution');
            stackTracer('serialize-error', errorObject);
        }
        return {
            action: 'ARM_DISARM_HANDLER',
            success: false,
            info: {
                error: errorObject,
            },
        };
    }
    async forceArmHandler(response, relativeUrl) {
        let errorObject;
        if (this.#internal.debug) {
            debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.forceArmHandler()', 'info', `Attempting to force arm on "${this.#internal.baseUrl}"`);
        }
        try {
            const sessions = {};
            if (typeof response.data !== 'string') {
                if (this.#internal.debug) {
                    debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.forceArmHandler()', 'error', 'The response body of the arm disarm page is not of type "string"');
                }
                return {
                    action: 'FORCE_ARM_HANDLER',
                    success: false,
                    info: {
                        message: 'The response body of the arm disarm page is not of type "string"',
                    },
                };
            }
            sessions.jsdomArmDisarm = new JSDOM(response.data, {
                url: response.config.url,
                referrer: response.config.headers.Referer,
                contentType: 'text/html',
                pretendToBeVisual: true,
            });
            const jsdomArmDisarmDoSubmitHandlers = sessions.jsdomArmDisarm.window.document.querySelectorAll('.p_whiteBoxMiddleCenter .p_armDisarmWrapper input');
            const jsdomArmDisarmArmDisarmMessage = sessions.jsdomArmDisarm.window.document.querySelector('.p_armDisarmWrapper div:first-child');
            const parsedArmDisarmMessage = parseArmDisarmMessage(jsdomArmDisarmArmDisarmMessage);
            const parsedDoSubmitHandlers = parseDoSubmitHandlers(jsdomArmDisarmDoSubmitHandlers);
            await this.newInformationDispatcher('do-submit-handlers', parsedDoSubmitHandlers);
            await this.newInformationDispatcher('debug-parser', {
                method: 'forceArmHandler',
                response: parsedDoSubmitHandlers,
                rawHtml: response.data,
            });
            if (parsedDoSubmitHandlers.length === 0) {
                if (this.#internal.testMode.enabled) {
                    if (this.#internal.debug) {
                        debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.forceArmHandler()', 'error', 'Test mode is active but no doors or windows were open');
                    }
                    return {
                        action: 'FORCE_ARM_HANDLER',
                        success: false,
                        info: {
                            message: 'Test mode is active but no doors or windows were open',
                        },
                    };
                }
                if (this.#internal.debug) {
                    debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.forceArmHandler()', 'info', 'Force arming not required');
                }
                return {
                    action: 'FORCE_ARM_HANDLER',
                    success: true,
                    info: {
                        forceArmRequired: false,
                    },
                };
            }
            const tracker = {
                complete: false,
                errorMessage: null,
                requestUrl: null,
            };
            if (this.#internal.debug) {
                debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.forceArmHandler()', 'warn', `Portal message ➜ "${parsedArmDisarmMessage}"`);
            }
            for (let i = 0; i < parsedDoSubmitHandlers.length; i += 1) {
                const forceArmRelativeUrl = parsedDoSubmitHandlers[i].relativeUrl;
                const forceArmSat = parsedDoSubmitHandlers[i].urlParams.sat;
                const forceArmHref = parsedDoSubmitHandlers[i].urlParams.href;
                const forceArmArmState = parsedDoSubmitHandlers[i].urlParams.armState;
                const forceArmArm = parsedDoSubmitHandlers[i].urlParams.arm;
                if ((tracker.complete
                    && tracker.errorMessage === null)
                    || forceArmArmState === null
                    || forceArmArm === null) {
                    continue;
                }
                const forceArmForm = new URLSearchParams();
                forceArmForm.append('sat', forceArmSat);
                forceArmForm.append('href', forceArmHref);
                forceArmForm.append('armstate', forceArmArmState);
                forceArmForm.append('arm', forceArmArm);
                sessions.axiosForceArm = await this.#session.httpClient.post(this.#internal.baseUrl + forceArmRelativeUrl, forceArmForm, this.getRequestConfig({
                    headers: {
                        Accept: '*/*',
                        'Content-type': 'application/x-www-form-urlencoded',
                        Origin: this.#internal.baseUrl,
                        Referer: `${this.#internal.baseUrl}/myhome/${this.#session.portalVersion}/${relativeUrl}`,
                        'Sec-Fetch-Dest': 'empty',
                        'Sec-Fetch-Mode': 'cors',
                        'Sec-Fetch-Site': 'same-origin',
                        'Sec-Fetch-User': undefined,
                        'x-dtpc': generateFakeDynatracePCHeaderValue('force-arm'),
                    },
                }));
                if (sessions.axiosForceArm.status >= 400) {
                    if (this.#internal.debug) {
                        debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.forceArmHandler()', 'error', `The remote server responded with a HTTP ${sessions.axiosForceArm.status} status code`);
                    }
                    return {
                        action: 'FORCE_ARM_HANDLER',
                        success: false,
                        info: {
                            message: `The remote server responded with a HTTP ${sessions.axiosForceArm.status} status code`,
                        },
                    };
                }
                if (typeof sessions.axiosForceArm?.request === 'undefined') {
                    if (this.#internal.debug) {
                        debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.forceArmHandler()', 'error', 'The HTTP client responded without the "request" object');
                    }
                    return {
                        action: 'FORCE_ARM_HANDLER',
                        success: false,
                        info: {
                            message: 'The HTTP client responded without the "request" object',
                        },
                    };
                }
                const axiosForceArmRequestPath = sessions.axiosForceArm.request.path;
                const axiosForceArmRequestPathValid = requestPathQuickControlServRunRraCommand.test(axiosForceArmRequestPath);
                if (this.#internal.debug) {
                    debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.forceArmHandler()', 'info', `Request path ➜ ${axiosForceArmRequestPath}`);
                    debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.forceArmHandler()', 'info', `Request path valid ➜ ${axiosForceArmRequestPathValid}`);
                }
                if (!axiosForceArmRequestPathValid) {
                    tracker.errorMessage = `"${axiosForceArmRequestPath}" is not the run rra command page`;
                    tracker.requestUrl = axiosForceArmRequestPath;
                    continue;
                }
                if (typeof sessions.axiosForceArm.data !== 'string') {
                    tracker.errorMessage = 'The response body of the run rra command page is not of type "string"';
                    tracker.requestUrl = axiosForceArmRequestPath;
                    continue;
                }
                if (!sessions.axiosForceArm.data.includes('1.0-OKAY')) {
                    tracker.errorMessage = 'The response body of the run rra command page does not include "1.0-OKAY"';
                    tracker.requestUrl = axiosForceArmRequestPath;
                    continue;
                }
                tracker.complete = true;
                tracker.errorMessage = null;
                tracker.requestUrl = null;
            }
            if (tracker.errorMessage !== null) {
                if (this.#internal.debug) {
                    debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.forceArmHandler()', 'error', tracker.errorMessage);
                }
                this.handleLoginFailure(tracker.requestUrl, sessions.axiosForceArm);
                return {
                    action: 'FORCE_ARM_HANDLER',
                    success: false,
                    info: {
                        message: tracker.errorMessage,
                    },
                };
            }
            if (!tracker.complete) {
                if (this.#internal.debug) {
                    debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.forceArmHandler()', 'error', 'Force arming failed because the "Arm Anyway" button was not found');
                }
                return {
                    action: 'FORCE_ARM_HANDLER',
                    success: false,
                    info: {
                        message: 'Force arming failed because the "Arm Anyway" button was not found',
                    },
                };
            }
            if (this.#internal.debug) {
                debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.forceArmHandler()', 'success', `Successfully forced arm on "${this.#internal.baseUrl}"`);
            }
            return {
                action: 'FORCE_ARM_HANDLER',
                success: true,
                info: {
                    forceArmRequired: true,
                },
            };
        }
        catch (error) {
            errorObject = (isErrorLike(error)) ? serializeError(error) : serializeError(new Error('Unknown error'));
        }
        if (this.#internal.debug) {
            debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.forceArmHandler()', 'error', 'Method encountered an error during execution');
            stackTracer('serialize-error', errorObject);
        }
        return {
            action: 'FORCE_ARM_HANDLER',
            success: false,
            info: {
                error: errorObject,
            },
        };
    }
    async newInformationDispatcher(type, data) {
        const dataHash = generateHash(data);
        if (this.#internal.reportedHashes.find((reportedHash) => dataHash === reportedHash) === undefined) {
            let detectedNew = false;
            switch (type) {
                case 'debug-parser':
                    detectedNew = await detectGlobalDebugParser(data, this.#internal.logger, this.#internal.debug);
                    break;
                case 'do-submit-handlers':
                    detectedNew = await detectApiDoSubmitHandlers(data, this.#internal.logger, this.#internal.debug);
                    break;
                case 'gateway-information':
                    detectedNew = await detectApiGatewayInformation(data, this.#internal.logger, this.#internal.debug);
                    break;
                case 'orb-security-buttons':
                    detectedNew = await detectApiOrbSecurityButtons(data, this.#internal.logger, this.#internal.debug);
                    break;
                case 'panel-information':
                    detectedNew = await detectApiPanelInformation(data, this.#internal.logger, this.#internal.debug);
                    break;
                case 'panel-status':
                    detectedNew = await detectApiPanelStatus(data, this.#internal.logger, this.#internal.debug);
                    break;
                case 'portal-version':
                    detectedNew = await detectGlobalPortalVersion(data, this.#internal.logger, this.#internal.debug);
                    break;
                case 'sensors-information':
                    detectedNew = await detectApiSensorsInformation(data, this.#internal.logger, this.#internal.debug);
                    break;
                case 'sensors-status':
                    detectedNew = await detectApiSensorsStatus(data, this.#internal.logger, this.#internal.debug);
                    break;
                default:
                    break;
            }
            if (detectedNew) {
                this.#internal.reportedHashes.push(dataHash);
            }
        }
    }
    getRequestConfig(extraConfig) {
        const defaultConfig = {
            family: 4,
            headers: {
                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',
                'Accept-Encoding': 'gzip, deflate, br, zstd',
                'Accept-Language': 'en-US,en;q=0.9',
                'Cache-Control': 'no-cache',
                Connection: 'keep-alive',
                Host: `${this.#connection.subdomain}.adtpulse.com`,
                Pragma: 'no-cache',
                'Sec-Fetch-Dest': 'document',
                'Sec-Fetch-Mode': 'navigate',
                'Sec-Fetch-Site': 'none',
                'Sec-Fetch-User': '?1',
                'Upgrade-Insecure-Requests': '1',
                'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36',
                'sec-ch-ua': '"Not/A)Brand";v="8", "Chromium";v="126", "Google Chrome";v="126"',
                'sec-ch-ua-mobile': '?0',
                'sec-ch-ua-platform': '"macOS"',
            },
            timeout: 15000,
        };
        if (extraConfig === undefined) {
            return defaultConfig;
        }
        return _.merge(_.omit(defaultConfig, findNullKeys(extraConfig)), _.omit(extraConfig, findNullKeys(extraConfig)));
    }
    handleLoginFailure(requestPath, session) {
        if (requestPath === null) {
            return;
        }
        if (requestPathAccessSignIn.test(requestPath)
            || requestPathAccessSignInEXxPartnerAdt.test(requestPath)
            || requestPathMfaMfaSignInWorkflowChallenge.test(requestPath)) {
            if (this.#internal.debug) {
                const errorMessage = fetchErrorMessage(session);
                if (requestPathAccessSignIn.test(requestPath) || requestPathAccessSignInEXxPartnerAdt.test(requestPath)) {
                    debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.handleLoginFailure()', 'error', 'Either the username or password is incorrect, fingerprint format is invalid, or was signed out due to inactivity');
                }
                if (requestPathMfaMfaSignInWorkflowChallenge.test(requestPath)) {
                    debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.handleLoginFailure()', 'error', 'Either the fingerprint was revoked or "Trust this device" was not selected after completing the multi-factor authentication challenge');
                }
                if (errorMessage !== null) {
                    debugLog(this.#internal.logger, 'api.ts / ADTPulseAPI.handleLoginFailure()', 'warn', `Portal message ➜ "${errorMessage}"`);
                }
            }
            this.resetSession();
        }
    }
}
//# sourceMappingURL=api.js.map