homebridge-adt-pulse 2.2.0 → 3.0.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  ISC License
2
2
 
3
- Copyright (c) 2019, Jacky Liang
3
+ Copyright (c) 2023, Jacky Liang
4
4
 
5
5
  Permission to use, copy, modify, and/or distribute this software for any
6
6
  purpose with or without fee is hereby granted, provided that the above
package/README.md CHANGED
@@ -8,6 +8,11 @@ ADT Pulse for Homebridge
8
8
 
9
9
  This is a [verified Homebridge plugin](https://github.com/homebridge/homebridge/wiki/verified-Plugins#verified-plugins) for ADT Pulse users that allow homeowners to control their security system and view sensor status through HomeKit. The API relies on the ADT Pulse Web Portal (by Icontrol One).
10
10
 
11
+ # Please install the beta version!
12
+ ### This plugin is completely re-written from the ground up, and I need everyone on board! Please install the pre-release version, so I can quickly get a faster and more stable version to you!
13
+ ### Everything is still very sloppy, please bear with me, even the readme has to be re-written.
14
+ ### In the meantime, do not use the UI to configure your plugin (the latest config is below). The UI uses v2 (this is now v3) config. All sensors will now HAVE TO be added manually to prevent automatic-adding/removals if ADT strikes a bug to break things again.
15
+
11
16
  To use this plugin, here are three simple steps you need to follow:
12
17
  1. Run `npm install homebridge-adt-pulse`
13
18
  2. Configure the plugin using the [configuration example](#configuration)
@@ -23,25 +28,20 @@ When configuring this plugin, simply add the platform to your existing `config.j
23
28
  {
24
29
  "platform": "ADTPulse",
25
30
  "name": "ADT Pulse",
31
+ "subdomain": "CAN BE 'portal' (USA) or 'portal-ca' (Canada)",
26
32
  "username": "email@email.com",
27
33
  "password": "1234567890",
28
34
  "fingerprint": "abcdef1234567890=",
29
- "overrideSensors": [
30
- {
31
- "name": "Sample Sensor 1",
32
- "type": "sensor,doorWindow"
33
- },
35
+ "sensors": [
34
36
  {
35
- "name": "...",
36
- "type": "..."
37
+ "name": "Lounge Smoke",
38
+ "adtName": "Basement Smoke",
39
+ "adtType": "fire",
40
+ "adtZone": 1
37
41
  }
38
42
  ],
39
- "country": "us",
40
- "logLevel": 30,
41
- "logActivity": true,
42
- "removeObsoleteZones": true,
43
- "pausePlugin": false,
44
- "resetAll": false
43
+ "pause": false,
44
+ "reset": false
45
45
  },
46
46
  {
47
47
  "platform": "...",
package/package.json CHANGED
@@ -1,13 +1,21 @@
1
1
  {
2
2
  "name": "homebridge-adt-pulse",
3
3
  "displayName": "Homebridge ADT Pulse",
4
- "version": "2.2.0",
4
+ "version": "3.0.0-beta.2",
5
5
  "description": "Homebridge security system platform for ADT Pulse",
6
- "main": "index.js",
6
+ "exports": "./build/src/index.js",
7
+ "type": "module",
7
8
  "private": false,
8
9
  "scripts": {
9
10
  "start": "homebridge --plugin-path $(pwd)",
10
- "start:debug": "homebridge --debug --plugin-path $(pwd)"
11
+ "debug": "homebridge --debug --plugin-path $(pwd)",
12
+ "build": "npm run build:cleanup && npm run build:tsc && npm run build:fix-paths",
13
+ "build:cleanup": "rimraf build",
14
+ "build:tsc": "tsc --project tsconfig.json",
15
+ "build:fix-paths": "tsconfig-replace-paths --project tsconfig.json",
16
+ "postinstall": "npm run build",
17
+ "repl": "node ./build/src/scripts/repl.js",
18
+ "test-api": "node ./build/src/scripts/test-api.js"
11
19
  },
12
20
  "repository": {
13
21
  "type": "git",
@@ -33,25 +41,38 @@
33
41
  "url": "https://github.com/mrjackyliang/homebridge-adt-pulse/issues"
34
42
  },
35
43
  "files": [
36
- "*.js",
37
- "*.schema.json"
44
+ "./src",
45
+ "config.schema.json"
38
46
  ],
39
- "homepage": "https://github.com/mrjackyliang/homebridge-adt-pulse#readme",
47
+ "homepage": "https://github.com/mrjackyliang/homebridge-adt-pulse",
40
48
  "engines": {
41
- "homebridge": ">=0.4.50",
42
- "node": ">=14.16.0"
49
+ "homebridge": "1.7.0",
50
+ "node": "18"
43
51
  },
44
52
  "dependencies": {
45
- "cheerio": "1.0.0-rc.5",
46
- "internet-available": "^1.0.0",
47
- "lodash": "^4.17.20",
48
- "q": "^1.5.1",
49
- "request": "^2.88.2"
53
+ "axios": "1.6.2",
54
+ "axios-cookiejar-support": "4.0.7",
55
+ "chalk": "5.3.0",
56
+ "jsdom": "23.0.1",
57
+ "lodash": "4.17.21",
58
+ "serialize-error": "11.0.3",
59
+ "tough-cookie": "4.1.3",
60
+ "zod": "3.22.4"
50
61
  },
51
62
  "devDependencies": {
52
- "eslint": "^6.8.0",
53
- "eslint-config-airbnb-base": "^14.2.1",
54
- "eslint-plugin-import": "^2.22.1",
55
- "typescript": "^3.9.7"
63
+ "@types/jsdom": "21.1.6",
64
+ "@types/lodash": "4.14.202",
65
+ "@types/node": "20.10.4",
66
+ "@types/tough-cookie": "4.0.5",
67
+ "@typescript-eslint/eslint-plugin": "6.13.2",
68
+ "@typescript-eslint/parser": "6.13.2",
69
+ "eslint": "8.55.0",
70
+ "eslint-config-airbnb-base": "15.0.0",
71
+ "eslint-config-airbnb-typescript": "17.1.0",
72
+ "eslint-plugin-import": "2.29.0",
73
+ "homebridge": "1.7.0",
74
+ "rimraf": "5.0.5",
75
+ "tsconfig-replace-paths": "0.0.14",
76
+ "typescript": "5.3.3"
56
77
  }
57
78
  }
package/src/index.ts ADDED
@@ -0,0 +1,18 @@
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;
@@ -0,0 +1,405 @@
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
+ }