homebridge-adt-pulse 3.0.0-beta.4 → 3.0.0-beta.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,171 @@
1
+ import chalk from 'chalk';
2
+ import _ from 'lodash';
3
+ import { readFileSync } from 'node:fs';
4
+ import os from 'node:os';
5
+ import { exit, stdin, stdout } from 'node:process';
6
+ import readline from 'node:readline';
7
+ import util from 'node:util';
8
+ import { ADTPulse } from '../lib/api.js';
9
+ import { platformConfig } from '../lib/schema.js';
10
+ import { debugLog, isForwardSlashOS } from '../lib/utility.js';
11
+ class ADTPulseTest {
12
+ #selectedConfigLocation;
13
+ #selectedPlatform;
14
+ async startTest() {
15
+ try {
16
+ const userAcceptedDisclaimer = await ADTPulseTest.askQuestion('disclaimer');
17
+ if (!userAcceptedDisclaimer) {
18
+ exit(0);
19
+ }
20
+ console.info('\r');
21
+ const configFoundAndSet = this.findConfig();
22
+ if (!configFoundAndSet || this.#selectedPlatform === undefined) {
23
+ ADTPulseTest.printTestOutput(false);
24
+ exit(1);
25
+ }
26
+ const instance = new ADTPulse(this.#selectedPlatform, {
27
+ debug: true,
28
+ testMode: {
29
+ enabled: true,
30
+ },
31
+ });
32
+ const instanceFunctions = [
33
+ instance.login.bind(instance),
34
+ instance.getGatewayInformation.bind(instance),
35
+ instance.getPanelInformation.bind(instance),
36
+ instance.getPanelStatus.bind(instance),
37
+ instance.setPanelStatus.bind(instance, 'away'),
38
+ instance.setPanelStatus.bind(instance, 'stay'),
39
+ instance.setPanelStatus.bind(instance, 'night'),
40
+ instance.setPanelStatus.bind(instance, 'off'),
41
+ instance.getSensorsInformation.bind(instance),
42
+ instance.getSensorsStatus.bind(instance),
43
+ instance.performSyncCheck.bind(instance),
44
+ instance.performKeepAlive.bind(instance),
45
+ instance.logout.bind(instance),
46
+ ];
47
+ for (let i = 0; i < instanceFunctions.length; i += 1) {
48
+ const response = await instanceFunctions[i]();
49
+ if (!response.success) {
50
+ ADTPulseTest.printTestOutput(false);
51
+ exit(1);
52
+ }
53
+ console.info(util.inspect(response, {
54
+ showHidden: false,
55
+ depth: Infinity,
56
+ colors: true,
57
+ }));
58
+ }
59
+ ADTPulseTest.printTestOutput(true);
60
+ exit(0);
61
+ }
62
+ catch {
63
+ ADTPulseTest.printTestOutput(false);
64
+ exit(1);
65
+ }
66
+ }
67
+ findConfig() {
68
+ const possibleLocations = [
69
+ ...(isForwardSlashOS()) ? [
70
+ '/homebridge/config.json',
71
+ '/var/lib/homebridge/config.json',
72
+ `${os.homedir()}/.homebridge/config.json`,
73
+ ] : [],
74
+ ...(!isForwardSlashOS()) ? [
75
+ `${os.homedir()}\\.homebridge\\config.json`,
76
+ ] : [],
77
+ ];
78
+ for (let i = 0; i < possibleLocations.length; i += 1) {
79
+ if (this.#selectedConfigLocation !== undefined && this.#selectedPlatform !== undefined) {
80
+ break;
81
+ }
82
+ debugLog(null, 'test-api.ts', 'info', `Attempt ${i + 1}: Finding the Homebridge config file in "${possibleLocations[i]}"`);
83
+ try {
84
+ const rawFile = readFileSync(possibleLocations[i], 'utf-8');
85
+ const parsedFile = JSON.parse(rawFile);
86
+ const platforms = _.get(parsedFile, ['platforms']);
87
+ const adtPlatform = _.find(platforms, (platform) => _.get(platform, ['platform']) === 'ADTPulse');
88
+ if (adtPlatform !== undefined) {
89
+ const validAdtPlatform = platformConfig.safeParse(adtPlatform);
90
+ if (validAdtPlatform.success) {
91
+ this.#selectedConfigLocation = possibleLocations[i];
92
+ this.#selectedPlatform = validAdtPlatform.data;
93
+ }
94
+ }
95
+ }
96
+ catch {
97
+ this.#selectedConfigLocation = undefined;
98
+ this.#selectedPlatform = undefined;
99
+ }
100
+ }
101
+ if (this.#selectedConfigLocation === undefined || this.#selectedPlatform === undefined) {
102
+ debugLog(null, 'test-api.ts', 'error', 'Unable to find a parsable Homebridge config file with a validated "ADTPulse" platform');
103
+ return false;
104
+ }
105
+ debugLog(null, 'test-api.ts', 'success', `Found valid Homebridge config in "${this.#selectedConfigLocation}"`);
106
+ return true;
107
+ }
108
+ static async askQuestion(mode) {
109
+ const rlInterface = readline.createInterface({
110
+ input: stdin,
111
+ output: stdout,
112
+ });
113
+ const questions = {
114
+ disclaimer: [
115
+ chalk.cyanBright('##############################################################'),
116
+ chalk.cyanBright('#### ADT Pulse for Homebridge Plugin Test ####'),
117
+ chalk.cyanBright('#### https://github.com/mrjackyliang/homebridge-adt-pulse ####'),
118
+ chalk.cyanBright('#### ####'),
119
+ chalk.cyanBright('#### Copyright (c) 2023 Jacky Liang. ISC License. ####'),
120
+ chalk.cyanBright('##############################################################'),
121
+ 'Before you begin, please make sure of the following:',
122
+ '',
123
+ '1. You have the proper authorization to carry out system testing.',
124
+ '2. You are currently ON THE PROPERTY where the test is being conducted.',
125
+ '3. You have disarmed the system and have >= 1 door/window open.',
126
+ '4. You have access to MyADT and have placed the system in test mode.',
127
+ '',
128
+ `${chalk.redBright('WARNING')}: If you DO NOT have access to MyADT or CANNOT place the system into`,
129
+ 'test mode, please DO NOT PROCEED. The author is NOT RESPONSIBLE if the test causes',
130
+ 'an accidental trigger or if ADT agents/local authorities become involved.',
131
+ '',
132
+ `${chalk.bold.yellowBright('NOTICE')}: The API gathers anonymous analytics to detect potential bugs or issues.`,
133
+ ' All personally identifiable information redacted. You will see exactly what will be sent out.',
134
+ '',
135
+ chalk.yellowBright('Type "I Agree" (without quotes) to fully acknowledge that you have read through'),
136
+ chalk.yellowBright('and understood the instructions, and agree to the disclaimer above ...'),
137
+ '➜ ',
138
+ ].join('\n'),
139
+ };
140
+ return new Promise((resolve) => {
141
+ rlInterface.question(questions[mode], (input) => {
142
+ if (input !== 'I Agree') {
143
+ resolve(false);
144
+ }
145
+ resolve(true);
146
+ });
147
+ });
148
+ }
149
+ static printTestOutput(isSuccess) {
150
+ if (!isSuccess) {
151
+ console.info([
152
+ '',
153
+ chalk.redBright('########################################################################'),
154
+ chalk.redBright('##### Test has failed! Please check the error response #####'),
155
+ chalk.redBright('##### above, attempt to resolve them, then run this tester again #####'),
156
+ chalk.redBright('########################################################################'),
157
+ ].join('\n'));
158
+ return;
159
+ }
160
+ console.info([
161
+ '',
162
+ chalk.greenBright('########################################################################'),
163
+ chalk.greenBright('##### Test has completed! If you find my plugin useful #####'),
164
+ chalk.greenBright('##### please consider donating to my efforts via GitHub Sponsors #####'),
165
+ chalk.greenBright('########################################################################'),
166
+ ].join('\n'));
167
+ }
168
+ }
169
+ const instance = new ADTPulseTest();
170
+ await instance.startTest();
171
+ //# sourceMappingURL=test-api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test-api.js","sourceRoot":"","sources":["../../../src/scripts/test-api.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "homebridge-adt-pulse",
3
3
  "displayName": "Homebridge ADT Pulse",
4
- "version": "3.0.0-beta.4",
4
+ "version": "3.0.0-beta.5",
5
5
  "description": "Homebridge security system platform for ADT Pulse",
6
6
  "exports": "./build/src/index.js",
7
7
  "type": "module",
@@ -13,8 +13,8 @@
13
13
  "build:cleanup": "rimraf build",
14
14
  "build:tsc": "tsc --project tsconfig.json",
15
15
  "build:fix-paths": "tsconfig-replace-paths --project tsconfig.json",
16
- "preinstall": "npm install --production=false --ignore-scripts",
17
- "postinstall": "npm run build",
16
+ "prepare": "npm run build",
17
+ "prepublishOnly": "npm run build",
18
18
  "repl": "node ./build/src/scripts/repl.js",
19
19
  "test-api": "node ./build/src/scripts/test-api.js"
20
20
  },
@@ -42,9 +42,8 @@
42
42
  "url": "https://github.com/mrjackyliang/homebridge-adt-pulse/issues"
43
43
  },
44
44
  "files": [
45
- "./src",
46
- "config.schema.json",
47
- "tsconfig.json"
45
+ "./build",
46
+ "config.schema.json"
48
47
  ],
49
48
  "homepage": "https://github.com/mrjackyliang/homebridge-adt-pulse",
50
49
  "engines": {
package/src/index.ts DELETED
@@ -1,18 +0,0 @@
1
- import { ADTPulsePlatform } from '@/lib/platform.js';
2
- import type { InitializeApi, InitializeReturns } from '@/types/index.d.ts';
3
-
4
- /**
5
- * Initialize.
6
- *
7
- * @param {InitializeApi} api - Api.
8
- *
9
- * @returns {InitializeReturns}
10
- *
11
- * @since 1.0.0
12
- */
13
- function initialize(api: InitializeApi): InitializeReturns {
14
- api.registerPlatform('ADTPulse', ADTPulsePlatform);
15
- }
16
-
17
- // Tell Homebridge this is the starting point.
18
- export default initialize;
@@ -1,405 +0,0 @@
1
- import type {
2
- ADTPulseAccessoryAccessory,
3
- ADTPulseAccessoryApi,
4
- ADTPulseAccessoryCharacteristic,
5
- ADTPulseAccessoryConstructorAccessory,
6
- ADTPulseAccessoryConstructorApi,
7
- ADTPulseAccessoryConstructorCharacteristic,
8
- ADTPulseAccessoryConstructorInstance,
9
- ADTPulseAccessoryConstructorLog,
10
- ADTPulseAccessoryConstructorService,
11
- ADTPulseAccessoryConstructorState,
12
- ADTPulseAccessoryGetPanelStatusContext,
13
- ADTPulseAccessoryGetPanelStatusMode,
14
- ADTPulseAccessoryGetPanelStatusReturns,
15
- ADTPulseAccessoryGetSensorStatusContext,
16
- ADTPulseAccessoryGetSensorStatusReturns,
17
- ADTPulseAccessoryInstance,
18
- ADTPulseAccessoryLog,
19
- ADTPulseAccessoryServices,
20
- ADTPulseAccessorySetPanelStatusArm,
21
- ADTPulseAccessorySetPanelStatusContext,
22
- ADTPulseAccessorySetPanelStatusReturns,
23
- ADTPulseAccessoryState,
24
- } from '@/types/index.d.ts';
25
-
26
- /**
27
- * ADT Pulse Accessory.
28
- *
29
- * @since 1.0.0
30
- */
31
- export class ADTPulseAccessory {
32
- /**
33
- * ADT Pulse Accessory - Accessory.
34
- *
35
- * @private
36
- *
37
- * @since 1.0.0
38
- */
39
- #accessory: ADTPulseAccessoryAccessory;
40
-
41
- /**
42
- * ADT Pulse Accessory - Api.
43
- *
44
- * @private
45
- *
46
- * @since 1.0.0
47
- */
48
- #api: ADTPulseAccessoryApi;
49
-
50
- /**
51
- * ADT Pulse Accessory - Characteristic.
52
- *
53
- * @private
54
- *
55
- * @since 1.0.0
56
- */
57
- #characteristic: ADTPulseAccessoryCharacteristic;
58
-
59
- /**
60
- * ADT Pulse Accessory - Instance.
61
- *
62
- * @private
63
- *
64
- * @since 1.0.0
65
- */
66
- #instance: ADTPulseAccessoryInstance;
67
-
68
- /**
69
- * ADT Pulse Accessory - Log.
70
- *
71
- * @private
72
- *
73
- * @since 1.0.0
74
- */
75
- #log: ADTPulseAccessoryLog;
76
-
77
- /**
78
- * ADT Pulse Accessory - Services.
79
- *
80
- * @private
81
- *
82
- * @since 1.0.0
83
- */
84
- #services: ADTPulseAccessoryServices;
85
-
86
- /**
87
- * ADT Pulse Accessory - State.
88
- *
89
- * @private
90
- *
91
- * @since 1.0.0
92
- */
93
- readonly #state: ADTPulseAccessoryState;
94
-
95
- /**
96
- * ADT Pulse Accessory - Constructor.
97
- *
98
- * @param {ADTPulseAccessoryConstructorAccessory} accessory - Accessory.
99
- * @param {ADTPulseAccessoryConstructorState} state - State.
100
- * @param {ADTPulseAccessoryConstructorInstance} instance - Instance.
101
- * @param {ADTPulseAccessoryConstructorService} service - Service.
102
- * @param {ADTPulseAccessoryConstructorCharacteristic} characteristic - Characteristic.
103
- * @param {ADTPulseAccessoryConstructorApi} api - Api.
104
- * @param {ADTPulseAccessoryConstructorLog} log - Log.
105
- *
106
- * @since 1.0.0
107
- */
108
- constructor(accessory: ADTPulseAccessoryConstructorAccessory, state: ADTPulseAccessoryConstructorState, instance: ADTPulseAccessoryConstructorInstance, service: ADTPulseAccessoryConstructorService, characteristic: ADTPulseAccessoryConstructorCharacteristic, api: ADTPulseAccessoryConstructorApi, log: ADTPulseAccessoryConstructorLog) {
109
- this.#accessory = accessory;
110
- this.#api = api;
111
- this.#characteristic = characteristic;
112
- this.#instance = instance;
113
- this.#log = log;
114
- this.#services = {};
115
- this.#state = state;
116
-
117
- // Set the "AccessoryInformation" service.
118
- this.#services.Information = this.#accessory.getService(service.AccessoryInformation) ?? this.#accessory.addService(service.AccessoryInformation);
119
-
120
- // Set the "AccessoryInformation" characteristics.
121
- this.#services.Information
122
- .setCharacteristic(this.#characteristic.Identify, false)
123
- .setCharacteristic(this.#characteristic.Manufacturer, accessory.context.manufacturer ?? 'ADT')
124
- .setCharacteristic(this.#characteristic.Model, accessory.context.model ?? 'N/A')
125
- .setCharacteristic(this.#characteristic.Name, accessory.context.name)
126
- .setCharacteristic(this.#characteristic.SerialNumber, accessory.context.serial ?? 'N/A')
127
- .setCharacteristic(this.#characteristic.FirmwareRevision, accessory.context.firmware ?? 'N/A')
128
- .setCharacteristic(this.#characteristic.HardwareRevision, accessory.context.hardware ?? 'N/A')
129
- .setCharacteristic(this.#characteristic.SoftwareRevision, accessory.context.software ?? 'N/A');
130
-
131
- // Set the service associated with the accessory.
132
- switch (accessory.context.type) {
133
- case 'co':
134
- this.#services.Primary = this.#accessory.getService(service.CarbonMonoxideSensor) ?? this.#accessory.addService(service.CarbonMonoxideSensor);
135
- break;
136
- case 'doorWindow':
137
- this.#services.Primary = this.#accessory.getService(service.ContactSensor) ?? this.#accessory.addService(service.ContactSensor);
138
- break;
139
- case 'fire':
140
- this.#services.Primary = this.#accessory.getService(service.SmokeSensor) ?? this.#accessory.addService(service.SmokeSensor);
141
- break;
142
- case 'flood':
143
- this.#services.Primary = this.#accessory.getService(service.LeakSensor) ?? this.#accessory.addService(service.LeakSensor);
144
- break;
145
- case 'gateway':
146
- // No supported service available.
147
- break;
148
- case 'glass':
149
- this.#services.Primary = this.#accessory.getService(service.MotionSensor) ?? this.#accessory.addService(service.MotionSensor);
150
- break;
151
- case 'motion':
152
- this.#services.Primary = this.#accessory.getService(service.MotionSensor) ?? this.#accessory.addService(service.MotionSensor);
153
- break;
154
- case 'panel':
155
- this.#services.Primary = this.#accessory.getService(service.SecuritySystem) ?? this.#accessory.addService(service.SecuritySystem);
156
- break;
157
- case 'temperature':
158
- this.#services.Primary = this.#accessory.getService(service.TemperatureSensor) ?? this.#accessory.addService(service.TemperatureSensor);
159
- break;
160
- default:
161
- break;
162
- }
163
-
164
- // Check for missing services.
165
- if (this.#services.Primary === undefined) {
166
- // The "gateway" accessory does not need to be initialized further.
167
- if (accessory.context.type !== 'gateway') {
168
- this.#log.error(`Failed to initialize ${accessory.context.name} (id: ${accessory.context.id}, uuid: ${accessory.context.uuid}) accessory services ...`);
169
- }
170
-
171
- return;
172
- }
173
-
174
- // Set the characteristics associated with the specific type of accessory.
175
- switch (accessory.context.type) {
176
- case 'co':
177
- this.#services.Primary.getCharacteristic(this.#characteristic.CarbonMonoxideDetected)
178
- .onGet(() => this.getSensorStatus(accessory.context));
179
- break;
180
- case 'doorWindow':
181
- this.#services.Primary.getCharacteristic(this.#characteristic.ContactSensorState)
182
- .onGet(() => this.getSensorStatus(accessory.context));
183
- break;
184
- case 'fire':
185
- this.#services.Primary.getCharacteristic(this.#characteristic.SmokeDetected)
186
- .onGet(() => this.getSensorStatus(accessory.context));
187
- break;
188
- case 'flood':
189
- this.#services.Primary.getCharacteristic(this.#characteristic.LeakDetected)
190
- .onGet(() => this.getSensorStatus(accessory.context));
191
- break;
192
- case 'gateway':
193
- // No supported characteristic available.
194
- break;
195
- case 'glass':
196
- this.#services.Primary.getCharacteristic(this.#characteristic.MotionDetected)
197
- .onGet(() => this.getSensorStatus(accessory.context));
198
- break;
199
- case 'motion':
200
- this.#services.Primary.getCharacteristic(this.#characteristic.MotionDetected)
201
- .onGet(() => this.getSensorStatus(accessory.context));
202
- break;
203
- case 'panel':
204
- this.#services.Primary.getCharacteristic(this.#characteristic.SecuritySystemCurrentState)
205
- .onGet(() => this.getPanelStatus('current', accessory.context));
206
- this.#services.Primary.getCharacteristic(this.#characteristic.SecuritySystemTargetState)
207
- .onGet(() => this.getPanelStatus('target', accessory.context))
208
- .onSet((value) => this.setPanelStatus(value, accessory.context));
209
- break;
210
- case 'temperature':
211
- this.#services.Primary.getCharacteristic(this.#characteristic.CurrentTemperature)
212
- .onGet(() => this.getSensorStatus(accessory.context));
213
- break;
214
- default:
215
- break;
216
- }
217
- }
218
-
219
- /**
220
- * ADT Pulse Accessory - Get sensor status.
221
- *
222
- * @param {ADTPulseAccessoryGetSensorStatusContext} context - Context.
223
- *
224
- * @private
225
- *
226
- * @returns {ADTPulseAccessoryGetSensorStatusReturns}
227
- *
228
- * @since 1.0.0
229
- */
230
- private getSensorStatus(context: ADTPulseAccessoryGetSensorStatusContext): ADTPulseAccessoryGetSensorStatusReturns {
231
- const { name, type, zone } = context;
232
-
233
- const sensor = this.#state.data.sensorsStatus.find((sensorStatus) => zone !== null && sensorStatus.name === name && sensorStatus.zone === zone);
234
-
235
- // If the sensor is not found or sensor type is invalid.
236
- if (sensor === undefined || !['co', 'doorWindow', 'fire', 'flood', 'glass', 'motion', 'temperature'].includes(type)) {
237
- throw new this.#api.hap.HapStatusError(this.#api.hap.HAPStatus.RESOURCE_DOES_NOT_EXIST);
238
- }
239
-
240
- const { status } = sensor;
241
-
242
- // If the sensor is currently offline.
243
- if (status === 'Unknown') {
244
- throw new this.#api.hap.HapStatusError(this.#api.hap.HAPStatus.SERVICE_COMMUNICATION_FAILURE);
245
- }
246
-
247
- // Find the status based on the sensor type.
248
- switch (context.type) {
249
- case 'co':
250
- return this.#characteristic.CarbonMonoxideDetected.CO_LEVELS_NORMAL; // TODO Fake status, need more information from portal.
251
- // break; TODO Put this back later when I get the full information in.
252
- case 'doorWindow':
253
- if (status.includes('Open')) {
254
- return this.#characteristic.ContactSensorState.CONTACT_NOT_DETECTED;
255
- }
256
-
257
- if (status.includes('Closed')) {
258
- return this.#characteristic.ContactSensorState.CONTACT_DETECTED;
259
- }
260
- break;
261
- case 'fire':
262
- return this.#characteristic.SmokeDetected.SMOKE_NOT_DETECTED; // TODO Fake status, need more information from portal.
263
- // break; TODO Put this back later when I get the full information in.
264
- case 'flood':
265
- if (status.includes('ALARM')) {
266
- return this.#characteristic.LeakDetected.LEAK_DETECTED;
267
- }
268
-
269
- if (status.includes('Okay')) {
270
- return this.#characteristic.LeakDetected.LEAK_NOT_DETECTED; // TODO Not 100% sure yet.
271
- }
272
- break;
273
- case 'glass':
274
- if (status.includes('Okay')) {
275
- return false;
276
- }
277
-
278
- if (status.includes('Tripped')) {
279
- return true;
280
- }
281
- break;
282
- case 'motion':
283
- if (status.includes('No Motion')) {
284
- return false;
285
- }
286
-
287
- if (status.includes('Motion')) {
288
- return true;
289
- }
290
- break;
291
- case 'temperature':
292
- return 75; // TODO Fake status, need more information from portal.
293
- // break; TODO Put this back later when I get the full information in.
294
- default:
295
- break;
296
- }
297
-
298
- // Throw error if sensor type not found.
299
- throw new this.#api.hap.HapStatusError(this.#api.hap.HAPStatus.RESOURCE_DOES_NOT_EXIST);
300
- }
301
-
302
- /**
303
- * ADT Pulse Accessory - Get panel status.
304
- *
305
- * @param {ADTPulseAccessoryGetPanelStatusMode} mode - Mode.
306
- * @param {ADTPulseAccessoryGetPanelStatusContext} context - Context.
307
- *
308
- * @private
309
- *
310
- * @returns {ADTPulseAccessoryGetPanelStatusReturns}
311
- *
312
- * @since 1.0.0
313
- */
314
- private getPanelStatus(mode: ADTPulseAccessoryGetPanelStatusMode, context: ADTPulseAccessoryGetPanelStatusContext): ADTPulseAccessoryGetPanelStatusReturns {
315
- // If device is not a security panel.
316
- if (context.type !== 'panel') {
317
- throw new this.#api.hap.HapStatusError(this.#api.hap.HAPStatus.RESOURCE_DOES_NOT_EXIST);
318
- }
319
-
320
- // If panel has no status.
321
- if (
322
- this.#state.data.panelStatus === null
323
- || this.#state.data.panelStatus.state === null
324
- || this.#state.data.panelStatus.status === null
325
- ) {
326
- throw new this.#api.hap.HapStatusError(this.#api.hap.HAPStatus.SERVICE_COMMUNICATION_FAILURE);
327
- }
328
-
329
- const { state, status } = this.#state.data.panelStatus;
330
-
331
- // If panel state is "Service Unavailable".
332
- if (state === 'Status Unavailable') {
333
- throw new this.#api.hap.HapStatusError(this.#api.hap.HAPStatus.SERVICE_COMMUNICATION_FAILURE);
334
- }
335
-
336
- // If mode is "current" and panel status is "BURGLARY ALARM".
337
- if (mode === 'current' && status === 'BURGLARY ALARM') {
338
- return this.#characteristic.SecuritySystemCurrentState.ALARM_TRIGGERED;
339
- }
340
-
341
- // All the other panel states.
342
- switch (state) {
343
- case 'Armed Away':
344
- return (mode === 'current') ? this.#characteristic.SecuritySystemCurrentState.AWAY_ARM : this.#characteristic.SecuritySystemTargetState.AWAY_ARM;
345
- case 'Armed Stay':
346
- return (mode === 'current') ? this.#characteristic.SecuritySystemCurrentState.STAY_ARM : this.#characteristic.SecuritySystemTargetState.STAY_ARM;
347
- case 'Armed Night':
348
- return (mode === 'current') ? this.#characteristic.SecuritySystemCurrentState.NIGHT_ARM : this.#characteristic.SecuritySystemTargetState.NIGHT_ARM;
349
- case 'Disarmed':
350
- return (mode === 'current') ? this.#characteristic.SecuritySystemCurrentState.DISARMED : this.#characteristic.SecuritySystemTargetState.DISARM;
351
- default:
352
- break;
353
- }
354
-
355
- // If panel has unknown status.
356
- throw new this.#api.hap.HapStatusError(this.#api.hap.HAPStatus.SERVICE_COMMUNICATION_FAILURE);
357
- }
358
-
359
- /**
360
- * ADT Pulse Accessory - Set panel status.
361
- *
362
- * @param {ADTPulseAccessorySetPanelStatusArm} arm - Arm.
363
- * @param {ADTPulseAccessorySetPanelStatusContext} context - Context.
364
- *
365
- * @private
366
- *
367
- * @returns {ADTPulseAccessorySetPanelStatusReturns}
368
- *
369
- * @since 1.0.0
370
- */
371
- private async setPanelStatus(arm: ADTPulseAccessorySetPanelStatusArm, context: ADTPulseAccessorySetPanelStatusContext): ADTPulseAccessorySetPanelStatusReturns {
372
- let result;
373
-
374
- // If device is not a security panel.
375
- if (context.type !== 'panel') {
376
- throw new this.#api.hap.HapStatusError(this.#api.hap.HAPStatus.RESOURCE_DOES_NOT_EXIST);
377
- }
378
-
379
- // Set the panel status.
380
- switch (arm) {
381
- case this.#characteristic.SecuritySystemTargetState.STAY_ARM:
382
- result = await this.#instance.setPanelStatus('stay');
383
- break;
384
- case this.#characteristic.SecuritySystemTargetState.AWAY_ARM:
385
- result = await this.#instance.setPanelStatus('away');
386
- break;
387
- case this.#characteristic.SecuritySystemTargetState.NIGHT_ARM:
388
- result = await this.#instance.setPanelStatus('night');
389
- break;
390
- case this.#characteristic.SecuritySystemTargetState.DISARM:
391
- result = await this.#instance.setPanelStatus('off');
392
- break;
393
- default:
394
- break;
395
- }
396
-
397
- // If setting the panel status was successful.
398
- if (result && result.success) {
399
- return;
400
- }
401
-
402
- // If panel has unknown arm value.
403
- throw new this.#api.hap.HapStatusError(this.#api.hap.HAPStatus.INVALID_VALUE_IN_REQUEST);
404
- }
405
- }