homebridge-ratgdo 2.3.0 → 2.3.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/README.md CHANGED
@@ -14,7 +14,7 @@
14
14
  </DIV>
15
15
  </SPAN>
16
16
 
17
- `homebridge-ratgdo` is a [Homebridge](https://homebridge.io) plugin that makes Chamberlain, Liftmaster, and other garage door openers that utilize the Ratgdo hardware control board available to [Apple's](https://www.apple.com) [HomeKit](https://www.apple.com/ios/home) smart home platform. You can determine if your garage door opener by checking the [Ratgdo website](https://paulwieland.github.io/ratgdo/).
17
+ `homebridge-ratgdo` is a [Homebridge](https://homebridge.io) plugin that makes Chamberlain, Liftmaster, and other garage door openers that utilize the [Ratgdo hardware control board](https://paulwieland.github.io/ratgdo/) and [commercial variants](https://konnected.io/products/smart-garage-door-opener-blaq-myq-alternative) to [Apple's](https://www.apple.com) [HomeKit](https://www.apple.com/ios/home) smart home platform. You can determine if your garage door opener by checking the [Ratgdo website](https://paulwieland.github.io/ratgdo/).
18
18
 
19
19
  ## Why use this plugin for Ratgdo support in HomeKit?
20
20
  In a nutshell, the aim of this plugin for things to *just work* with minimal required configuration by users. The goal is to provide as close to a streamlined experience as you would expect from a first-party or native HomeKit solution. For the adventurous, those additional granular options are, of course, available to support more esoteric use cases or other unique needs.
@@ -33,6 +33,7 @@ In the interest of the community seeking a solution outside of myQ, I've develop
33
33
  * Ability to lock and unlock the garage door opener through the ability to lockout wireless remotes.
34
34
  * Read-only garage door opener support.
35
35
  * Automation switch and dimmer support, allowing you to set the garage door to any position.
36
+ * Support for Ratgdo variants like [Konnected blaQ](https://konnected.io/products/smart-garage-door-opener-blaq-myq-alternative).
36
37
  * A rich webUI for configuration.
37
38
 
38
39
  ## Installation
package/dist/index.js CHANGED
@@ -4,7 +4,7 @@
4
4
  */
5
5
  import { PLATFORM_NAME, PLUGIN_NAME } from "./settings.js";
6
6
  import { RatgdoPlatform } from "./ratgdo-platform.js";
7
- // Register our platform with homebridge.
7
+ // Register our platform with Homebridge.
8
8
  export default (api) => {
9
9
  api.registerPlatform(PLUGIN_NAME, PLATFORM_NAME, RatgdoPlatform);
10
10
  };
@@ -1,6 +1,6 @@
1
1
  import { PlatformAccessory } from "homebridge";
2
- import { RatgdoDevice } from "./ratgdo-types.js";
3
2
  import { HomebridgePluginLogging } from "homebridge-plugin-utils";
3
+ import { RatgdoDevice } from "./ratgdo-types.js";
4
4
  import { RatgdoPlatform } from "./ratgdo-platform.js";
5
5
  export declare class RatgdoAccessory {
6
6
  private readonly accessory;
@@ -38,7 +38,6 @@ export declare class RatgdoAccessory {
38
38
  private doorTargetStateBias;
39
39
  private lockTargetStateBias;
40
40
  private hasFeature;
41
- private setServiceName;
42
41
  private get name();
43
42
  private get accessoryName();
44
43
  private set accessoryName(value);
@@ -1,4 +1,5 @@
1
1
  import { FetchError, fetch } from "@adobe/fetch";
2
+ import { acquireService, validService } from "homebridge-plugin-utils";
2
3
  import { RATGDO_MOTION_DURATION, RATGDO_OCCUPANCY_DURATION } from "./settings.js";
3
4
  import { RatgdoReservedNames, RatgdoVariant } from "./ratgdo-types.js";
4
5
  import util from "node:util";
@@ -161,136 +162,114 @@ export class RatgdoAccessory {
161
162
  }
162
163
  // Configure the garage door service for HomeKit.
163
164
  configureGarageDoor() {
164
- let garageDoorService = this.accessory.getService(this.hap.Service.GarageDoorOpener);
165
- // Add the garage door opener service to the accessory, if needed.
166
- if (!garageDoorService) {
167
- garageDoorService = new this.hap.Service.GarageDoorOpener(this.name);
168
- garageDoorService.addOptionalCharacteristic(this.hap.Characteristic.ConfiguredName);
169
- this.accessory.addService(garageDoorService);
165
+ // Acquire the service.
166
+ const service = acquireService(this.hap, this.accessory, this.hap.Service.GarageDoorOpener, this.name);
167
+ if (!service) {
168
+ this.log.error("Unable to add the garage door.");
169
+ return false;
170
170
  }
171
171
  // Set the initial current and target door states to closed since ratgdo doesn't tell us initial state over MQTT on startup.
172
- garageDoorService.updateCharacteristic(this.hap.Characteristic.CurrentDoorState, this.status.door);
173
- garageDoorService.updateCharacteristic(this.hap.Characteristic.TargetDoorState, this.doorTargetStateBias(this.status.door));
172
+ service.updateCharacteristic(this.hap.Characteristic.CurrentDoorState, this.status.door);
173
+ service.updateCharacteristic(this.hap.Characteristic.TargetDoorState, this.doorTargetStateBias(this.status.door));
174
174
  // Handle HomeKit open and close events.
175
- garageDoorService.getCharacteristic(this.hap.Characteristic.TargetDoorState).onSet((value) => {
175
+ service.getCharacteristic(this.hap.Characteristic.TargetDoorState).onSet((value) => {
176
176
  this.setDoorState(value);
177
177
  });
178
178
  // Inform HomeKit of our current state.
179
- garageDoorService.getCharacteristic(this.hap.Characteristic.CurrentDoorState).onGet(() => this.status.door);
179
+ service.getCharacteristic(this.hap.Characteristic.CurrentDoorState).onGet(() => this.status.door);
180
180
  // Inform HomeKit of any obstructions.
181
- garageDoorService.getCharacteristic(this.hap.Characteristic.ObstructionDetected).onGet(() => this.status.obstruction === true);
181
+ service.getCharacteristic(this.hap.Characteristic.ObstructionDetected).onGet(() => this.status.obstruction === true);
182
182
  // Configure the lock garage door lock current and target state characteristics.
183
- garageDoorService.getCharacteristic(this.hap.Characteristic.LockTargetState).onSet(async (value) => {
183
+ service.getCharacteristic(this.hap.Characteristic.LockTargetState).onSet(async (value) => {
184
184
  if (!(await this.command("lock", (value === this.hap.Characteristic.LockTargetState.SECURED) ? "lock" : "unlock"))) {
185
185
  // Something went wrong. Let's make sure we revert the lock to it's prior state.
186
186
  setTimeout(() => {
187
- garageDoorService?.updateCharacteristic(this.hap.Characteristic.LockTargetState, this.lockTargetStateBias(this.status.lock));
188
- garageDoorService?.updateCharacteristic(this.hap.Characteristic.LockCurrentState, this.status.lock);
187
+ service?.updateCharacteristic(this.hap.Characteristic.LockTargetState, this.lockTargetStateBias(this.status.lock));
188
+ service?.updateCharacteristic(this.hap.Characteristic.LockCurrentState, this.status.lock);
189
189
  }, 50);
190
190
  }
191
191
  });
192
- garageDoorService.updateCharacteristic(this.hap.Characteristic.LockCurrentState, this.status.lock);
193
- garageDoorService.updateCharacteristic(this.hap.Characteristic.LockTargetState, this.lockTargetStateBias(this.status.lock));
194
- garageDoorService.getCharacteristic(this.hap.Characteristic.StatusActive).onGet(() => this.status.availability);
195
- garageDoorService.updateCharacteristic(this.hap.Characteristic.StatusActive, this.status.availability);
192
+ service.updateCharacteristic(this.hap.Characteristic.LockCurrentState, this.status.lock);
193
+ service.updateCharacteristic(this.hap.Characteristic.LockTargetState, this.lockTargetStateBias(this.status.lock));
194
+ service.getCharacteristic(this.hap.Characteristic.StatusActive).onGet(() => this.status.availability);
195
+ service.updateCharacteristic(this.hap.Characteristic.StatusActive, this.status.availability);
196
196
  // Let HomeKit know that this is the primary service on this accessory.
197
- garageDoorService.setPrimaryService(true);
197
+ service.setPrimaryService(true);
198
198
  return true;
199
199
  }
200
200
  // Configure the light for HomeKit.
201
201
  configureLight() {
202
- // Find the service, if it exists.
203
- let lightService = this.accessory.getService(this.hap.Service.Lightbulb);
204
- // Have we disabled the light?
205
- if (!this.hints.light) {
206
- if (lightService) {
207
- this.accessory.removeService(lightService);
208
- this.log.info("Disabling light.");
202
+ // Validate whether we should have this service enabled.
203
+ if (!validService(this.accessory, this.hap.Service.Lightbulb, () => {
204
+ // Have we disabled the light?
205
+ if (!this.hints.light) {
206
+ this.log.info("Disabling the light.");
207
+ return false;
209
208
  }
209
+ return true;
210
+ })) {
210
211
  return false;
211
212
  }
212
- // Add the service to the accessory, if needed.
213
- if (!lightService) {
214
- lightService = new this.hap.Service.Lightbulb(this.name);
215
- if (!lightService) {
216
- this.log.error("Unable to add the light.");
217
- return false;
218
- }
219
- lightService.addOptionalCharacteristic(this.hap.Characteristic.ConfiguredName);
220
- lightService.updateCharacteristic(this.hap.Characteristic.Name, this.name);
221
- this.setServiceName(lightService, this.name);
222
- this.accessory.addService(lightService);
223
- this.log.info("Enabling light.");
213
+ // Acquire the service.
214
+ const service = acquireService(this.hap, this.accessory, this.hap.Service.Lightbulb, this.name, undefined, () => this.log.info("Enabling light."));
215
+ if (!service) {
216
+ this.log.error("Unable to add the light.");
217
+ return false;
224
218
  }
225
219
  // Initialize the light.
226
- lightService.updateCharacteristic(this.hap.Characteristic.On, this.status.light);
220
+ service.updateCharacteristic(this.hap.Characteristic.On, this.status.light);
227
221
  // Turn the light on or off.
228
- lightService.getCharacteristic(this.hap.Characteristic.On)?.onGet(() => this.status.light);
229
- lightService.getCharacteristic(this.hap.Characteristic.On)?.onSet((value) => {
230
- void this.command("light", value === true ? "on" : "off");
231
- });
222
+ service.getCharacteristic(this.hap.Characteristic.On)?.onGet(() => this.status.light);
223
+ service.getCharacteristic(this.hap.Characteristic.On)?.onSet((value) => void this.command("light", value === true ? "on" : "off"));
232
224
  return true;
233
225
  }
234
226
  // Configure the motion sensor for HomeKit.
235
227
  configureMotionSensor() {
236
- // Find the motion sensor service, if it exists.
237
- let motionService = this.accessory.getService(this.hap.Service.MotionSensor);
238
- // Have we disabled the motion sensor?
239
- if (!this.hints.motionSensor) {
240
- if (motionService) {
241
- this.accessory.removeService(motionService);
242
- this.log.info("Disabling motion sensor.");
228
+ // Validate whether we should have this service enabled.
229
+ if (!validService(this.accessory, this.hap.Service.MotionSensor, () => {
230
+ // Have we disabled the motion sensor?
231
+ if (!this.hints.motionSensor) {
232
+ this.log.info("Disabling the motion sensor.");
233
+ return false;
243
234
  }
235
+ return true;
236
+ })) {
244
237
  return false;
245
238
  }
246
- // We don't have a motion sensor, let's add it to the device.
247
- if (!motionService) {
248
- // We don't have it, add the motion sensor to the device.
249
- motionService = new this.hap.Service.MotionSensor(this.name);
250
- if (!motionService) {
251
- this.log.error("Unable to add the motion sensor.");
252
- return false;
253
- }
254
- motionService.addOptionalCharacteristic(this.hap.Characteristic.ConfiguredName);
255
- motionService.updateCharacteristic(this.hap.Characteristic.Name, this.name);
256
- this.setServiceName(motionService, this.name);
257
- this.accessory.addService(motionService);
258
- this.log.info("Enabling motion sensor.");
239
+ // Acquire the service.
240
+ const service = acquireService(this.hap, this.accessory, this.hap.Service.MotionSensor, this.name, undefined, () => this.log.info("Enabling motion sensor."));
241
+ if (!service) {
242
+ this.log.error("Unable to add the motion sensor.");
243
+ return false;
259
244
  }
260
245
  // Initialize the state of the motion sensor.
261
- motionService.updateCharacteristic(this.hap.Characteristic.MotionDetected, false);
262
- motionService.updateCharacteristic(this.hap.Characteristic.StatusActive, this.status.availability);
263
- motionService.getCharacteristic(this.hap.Characteristic.StatusActive).onGet(() => this.status.availability);
246
+ service.updateCharacteristic(this.hap.Characteristic.MotionDetected, false);
247
+ service.updateCharacteristic(this.hap.Characteristic.StatusActive, this.status.availability);
248
+ service.getCharacteristic(this.hap.Characteristic.StatusActive).onGet(() => this.status.availability);
264
249
  return true;
265
250
  }
266
251
  // Configure a dimmer to automate open and close events in HomeKit beyond what HomeKit might allow for a garage opener service that gets treated as a secure service.
267
252
  configureAutomationDoorPositionDimmer() {
268
- // Find the dimmer service, if it exists.
269
- let dimmerService = this.accessory.getServiceById(this.hap.Service.Lightbulb, RatgdoReservedNames.DIMMER_OPENER_AUTOMATION);
270
- // The switch is disabled by default and primarily exists for automation purposes.
271
- if (!this.hints.automationDimmer) {
272
- if (dimmerService) {
273
- this.accessory.removeService(dimmerService);
274
- this.log.info("Disabling automation door position dimmer.");
253
+ // Validate whether we should have this service enabled.
254
+ if (!validService(this.accessory, this.hap.Service.Lightbulb, () => {
255
+ // The door position dimmer is disabled by default and primarily exists for automation purposes.
256
+ if (!this.hints.automationDimmer) {
257
+ return false;
275
258
  }
259
+ return true;
260
+ }, RatgdoReservedNames.DIMMER_OPENER_AUTOMATION)) {
276
261
  return false;
277
262
  }
278
- // Add the dimmer to the opener, if needed.
279
- if (!dimmerService) {
280
- dimmerService = new this.hap.Service.Lightbulb(this.name + " Automation Door Position", RatgdoReservedNames.DIMMER_OPENER_AUTOMATION);
281
- if (!dimmerService) {
282
- this.log.error("Unable to add automation door position dimmer.");
283
- return false;
284
- }
285
- this.accessory.addService(dimmerService);
263
+ // Acquire the service.
264
+ const service = acquireService(this.hap, this.accessory, this.hap.Service.Lightbulb, this.name + " Automation Door Position", RatgdoReservedNames.DIMMER_OPENER_AUTOMATION);
265
+ if (!service) {
266
+ this.log.error("Unable to add the automation door position dimmer.");
267
+ return false;
286
268
  }
287
- // Return the current state of the opener.
288
- dimmerService.getCharacteristic(this.hap.Characteristic.On)?.onGet(() => {
289
- // We're on if we are in any state other than closed (specifically open or stopped).
290
- return this.doorCurrentStateBias(this.status.door) !== this.hap.Characteristic.CurrentDoorState.CLOSED;
291
- });
269
+ // Return the current state of the opener. We're on if we are in any state other than closed (specifically open or stopped).
270
+ service.getCharacteristic(this.hap.Characteristic.On)?.onGet(() => this.doorCurrentStateBias(this.status.door) !== this.hap.Characteristic.CurrentDoorState.CLOSED);
292
271
  // Close the opener. Opening is really handled in the brightness event.
293
- dimmerService.getCharacteristic(this.hap.Characteristic.On)?.onSet((value) => {
272
+ service.getCharacteristic(this.hap.Characteristic.On)?.onSet((value) => {
294
273
  // We really only want to act when the opener is open. Otherwise, it's handled by the brightness event.
295
274
  if (value) {
296
275
  return;
@@ -302,60 +281,47 @@ export class RatgdoAccessory {
302
281
  // Send the command.
303
282
  if (!this.setDoorState(this.hap.Characteristic.TargetDoorState.CLOSED)) {
304
283
  // Something went wrong. Let's make sure we revert the dimmer to it's prior state.
305
- setTimeout(() => {
306
- dimmerService?.updateCharacteristic(this.hap.Characteristic.On, !value);
307
- }, 50);
284
+ setTimeout(() => service?.updateCharacteristic(this.hap.Characteristic.On, !value), 50);
308
285
  }
309
286
  });
310
287
  // Return the door position of the opener.
311
- dimmerService.getCharacteristic(this.hap.Characteristic.Brightness)?.onGet(() => {
312
- return this.status.doorPosition;
313
- });
288
+ service.getCharacteristic(this.hap.Characteristic.Brightness)?.onGet(() => this.status.doorPosition);
314
289
  // Adjust the door position of the opener by adjusting brightness of the light.
315
- dimmerService.getCharacteristic(this.hap.Characteristic.Brightness)?.onSet((value) => {
290
+ service.getCharacteristic(this.hap.Characteristic.Brightness)?.onSet((value) => {
316
291
  if (this.hints.logOpener) {
317
292
  this.log.info("Automation door position dimmer: moving opener to %s%.", value.toFixed(0));
318
293
  }
319
294
  this.setDoorState(value > 0 ?
320
295
  this.hap.Characteristic.TargetDoorState.OPEN : this.hap.Characteristic.TargetDoorState.CLOSED, value);
321
296
  });
322
- // Initialize the switch.
323
- dimmerService.displayName = this.name + " Automation Door Position";
324
- dimmerService.updateCharacteristic(this.hap.Characteristic.Name, this.name + " Automation Door Position");
325
- dimmerService.updateCharacteristic(this.hap.Characteristic.On, this.doorCurrentStateBias(this.status.door) !== this.hap.Characteristic.CurrentDoorState.CLOSED);
326
- dimmerService.updateCharacteristic(this.hap.Characteristic.Brightness, this.status.doorPosition);
327
- this.log.info("Enabling automation door position dimmer.");
297
+ // Initialize the dimmer.
298
+ service.updateCharacteristic(this.hap.Characteristic.On, this.doorCurrentStateBias(this.status.door) !== this.hap.Characteristic.CurrentDoorState.CLOSED);
299
+ service.updateCharacteristic(this.hap.Characteristic.Brightness, this.status.doorPosition);
300
+ this.log.info("Enabling the automation door position dimmer.");
328
301
  return true;
329
302
  }
330
303
  // Configure a switch to automate open and close events in HomeKit beyond what HomeKit might allow for a garage opener service that gets treated as a secure service.
331
304
  configureAutomationDoorSwitch() {
332
- // Find the switch service, if it exists.
333
- let switchService = this.accessory.getServiceById(this.hap.Service.Switch, RatgdoReservedNames.SWITCH_OPENER_AUTOMATION);
334
- // The switch is disabled by default and primarily exists for automation purposes.
335
- if (!this.hints.automationSwitch) {
336
- if (switchService) {
337
- this.accessory.removeService(switchService);
338
- this.log.info("Disabling automation door opener switch.");
305
+ // Validate whether we should have this service enabled.
306
+ if (!validService(this.accessory, this.hap.Service.Switch, () => {
307
+ // Have we disabled the automation switch?
308
+ if (!this.hints.automationSwitch) {
309
+ return false;
339
310
  }
311
+ return true;
312
+ }, RatgdoReservedNames.SWITCH_OPENER_AUTOMATION)) {
340
313
  return false;
341
314
  }
342
- // Add the switch to the opener, if needed.
343
- if (!switchService) {
344
- switchService = new this.hap.Service.Switch(this.name + " Automation Opener", RatgdoReservedNames.SWITCH_OPENER_AUTOMATION);
345
- if (!switchService) {
346
- this.log.error("Unable to add automation door opener switch.");
347
- return false;
348
- }
349
- switchService.addOptionalCharacteristic(this.hap.Characteristic.ConfiguredName);
350
- this.accessory.addService(switchService);
315
+ // Acquire the service.
316
+ const service = acquireService(this.hap, this.accessory, this.hap.Service.Switch, this.name + " Automation Opener", RatgdoReservedNames.SWITCH_OPENER_AUTOMATION);
317
+ if (!service) {
318
+ this.log.error("Unable to add the automation door opener switch.");
319
+ return false;
351
320
  }
352
- // Return the current state of the opener.
353
- switchService.getCharacteristic(this.hap.Characteristic.On)?.onGet(() => {
354
- // We're on if we are in any state other than closed (specifically open or stopped).
355
- return this.doorCurrentStateBias(this.status.door) !== this.hap.Characteristic.CurrentDoorState.CLOSED;
356
- });
321
+ // Return the current state of the opener. We're on if we are in any state other than closed (specifically open or stopped).
322
+ service.getCharacteristic(this.hap.Characteristic.On)?.onGet(() => this.doorCurrentStateBias(this.status.door) !== this.hap.Characteristic.CurrentDoorState.CLOSED);
357
323
  // Open or close the opener.
358
- switchService.getCharacteristic(this.hap.Characteristic.On)?.onSet((value) => {
324
+ service.getCharacteristic(this.hap.Characteristic.On)?.onSet((value) => {
359
325
  // Inform the user.
360
326
  if (this.hints.logOpener) {
361
327
  this.log.info("Automation door opener switch: %s.", value ? "open" : "close");
@@ -363,128 +329,97 @@ export class RatgdoAccessory {
363
329
  // Send the command.
364
330
  if (!this.setDoorState(value ? this.hap.Characteristic.TargetDoorState.OPEN : this.hap.Characteristic.TargetDoorState.CLOSED)) {
365
331
  // Something went wrong. Let's make sure we revert the switch to it's prior state.
366
- setTimeout(() => {
367
- switchService?.updateCharacteristic(this.hap.Characteristic.On, !value);
368
- }, 50);
332
+ setTimeout(() => service?.updateCharacteristic(this.hap.Characteristic.On, !value), 50);
369
333
  }
370
334
  });
371
335
  // Initialize the switch.
372
- switchService.updateCharacteristic(this.hap.Characteristic.Name, this.name + " Automation Opener");
373
- this.setServiceName(switchService, this.name + " Automation Opener");
374
- switchService.updateCharacteristic(this.hap.Characteristic.On, this.doorCurrentStateBias(this.status.door) !== this.hap.Characteristic.CurrentDoorState.CLOSED);
375
- this.log.info("Enabling automation door opener switch.");
336
+ service.updateCharacteristic(this.hap.Characteristic.On, this.doorCurrentStateBias(this.status.door) !== this.hap.Characteristic.CurrentDoorState.CLOSED);
337
+ this.log.info("Enabling the automation door opener switch.");
376
338
  return true;
377
339
  }
378
340
  // Configure a switch to control the ability to lockout all wireless remotes for the garage door opener, if the feature exists.
379
341
  configureAutomationLockoutSwitch() {
380
- // Find the switch service, if it exists.
381
- let switchService = this.accessory.getServiceById(this.hap.Service.Switch, RatgdoReservedNames.SWITCH_LOCKOUT);
382
- // The switch is disabled by default and primarily exists for automation purposes.
383
- if (!this.hints.lockoutSwitch) {
384
- if (switchService) {
385
- this.accessory.removeService(switchService);
386
- this.log.info("Disabling automation wireless remote lockout switch.");
342
+ // Validate whether we should have this service enabled.
343
+ if (!validService(this.accessory, this.hap.Service.Switch, () => {
344
+ // The wireless lockout switch is disabled by default and primarily exists for automation purposes.
345
+ if (!this.hints.lockoutSwitch) {
346
+ return false;
387
347
  }
348
+ return true;
349
+ }, RatgdoReservedNames.SWITCH_LOCKOUT)) {
388
350
  return false;
389
351
  }
390
- // Add the switch to the opener, if needed.
391
- if (!switchService) {
392
- switchService = new this.hap.Service.Switch(this.name + " Lockout", RatgdoReservedNames.SWITCH_LOCKOUT);
393
- if (!switchService) {
394
- this.log.error("Unable to add automation wireless remote lockout switch.");
395
- return false;
396
- }
397
- switchService.addOptionalCharacteristic(this.hap.Characteristic.ConfiguredName);
398
- this.accessory.addService(switchService);
352
+ // Acquire the service.
353
+ const service = acquireService(this.hap, this.accessory, this.hap.Service.Switch, this.name + " Lockout", RatgdoReservedNames.SWITCH_LOCKOUT);
354
+ if (!service) {
355
+ this.log.error("Unable to add the automation wireless remote lockout switch.");
356
+ return false;
399
357
  }
400
- // Return the current state of the opener.
401
- switchService.getCharacteristic(this.hap.Characteristic.On)?.onGet(() => {
402
- // We're on if we are in any state other than locked.
403
- return this.status.lock === this.hap.Characteristic.LockCurrentState.SECURED;
404
- });
405
- // Open or close the opener.
406
- switchService.getCharacteristic(this.hap.Characteristic.On)?.onSet(async (value) => {
358
+ // Return the current state of the opener. We're on if we are in any state other than locked.
359
+ service.getCharacteristic(this.hap.Characteristic.On)?.onGet(() => this.status.lock === this.hap.Characteristic.LockCurrentState.SECURED);
360
+ // Lock or unlock the wireless remotes.
361
+ service.getCharacteristic(this.hap.Characteristic.On)?.onSet(async (value) => {
407
362
  // Inform the user.
408
363
  this.log.info("Automation wireless remote lockout switch: remotes are %s.", value ? "locked out" : "permitted");
409
364
  // Send the command.
410
365
  if (!(await this.command("lock", value ? "lock" : "unlock"))) {
411
366
  // Something went wrong. Let's make sure we revert the switch to it's prior state.
412
- setTimeout(() => {
413
- switchService?.updateCharacteristic(this.hap.Characteristic.On, !value);
414
- }, 50);
367
+ setTimeout(() => service?.updateCharacteristic(this.hap.Characteristic.On, !value), 50);
415
368
  }
416
369
  });
417
370
  // Initialize the switch.
418
- switchService.updateCharacteristic(this.hap.Characteristic.Name, this.name + " Lockout");
419
- this.setServiceName(switchService, this.name + " Lockout");
420
- switchService.updateCharacteristic(this.hap.Characteristic.On, this.status.lock === this.hap.Characteristic.LockCurrentState.SECURED);
421
- this.log.info("Enabling automation wireless remote lockout switch.");
371
+ service.updateCharacteristic(this.hap.Characteristic.On, this.status.lock === this.hap.Characteristic.LockCurrentState.SECURED);
372
+ this.log.info("Enabling the automation wireless remote lockout switch.");
422
373
  return true;
423
374
  }
424
375
  // Configure the door open occupancy sensor for HomeKit.
425
376
  configureDoorOpenOccupancySensor() {
426
- // Find the occupancy sensor service, if it exists.
427
- let occupancyService = this.accessory.getServiceById(this.hap.Service.OccupancySensor, RatgdoReservedNames.OCCUPANCY_SENSOR_DOOR_OPEN);
428
- // The occupancy sensor is disabled by default and primarily exists for automation purposes.
429
- if (!this.hints.doorOpenOccupancySensor) {
430
- if (occupancyService) {
431
- this.accessory.removeService(occupancyService);
432
- this.log.info("Disabling door open indicator occupancy sensor.");
377
+ // Validate whether we should have this service enabled.
378
+ if (!validService(this.accessory, this.hap.Service.OccupancySensor, () => {
379
+ // The occupancy sensor is disabled by default and primarily exists for automation purposes.
380
+ if (!this.hints.doorOpenOccupancySensor) {
381
+ return false;
433
382
  }
383
+ return true;
384
+ }, RatgdoReservedNames.OCCUPANCY_SENSOR_DOOR_OPEN)) {
434
385
  return false;
435
386
  }
436
- // We don't have an occupancy sensor, let's add it to the device.
437
- if (!occupancyService) {
438
- // We don't have it, add the occupancy sensor to the device.
439
- occupancyService = new this.hap.Service.OccupancySensor(this.name + " Open", RatgdoReservedNames.OCCUPANCY_SENSOR_DOOR_OPEN);
440
- if (!occupancyService) {
441
- this.log.error("Unable to add door open occupancy sensor.");
442
- return false;
443
- }
444
- occupancyService.addOptionalCharacteristic(this.hap.Characteristic.ConfiguredName);
445
- this.setServiceName(occupancyService, this.name + " Open");
446
- this.accessory.addService(occupancyService);
447
- }
448
- // Initialize the state of the occupancy sensor.
449
- occupancyService.updateCharacteristic(this.hap.Characteristic.OccupancyDetected, false);
450
- occupancyService.updateCharacteristic(this.hap.Characteristic.StatusActive, this.status.availability);
451
- occupancyService.getCharacteristic(this.hap.Characteristic.StatusActive).onGet(() => {
452
- return this.status.availability;
453
- });
454
- this.log.info("Enabling door open indicator occupancy sensor. Occupancy will be triggered when the opener has been continuously open for more than %s seconds.", this.hints.doorOpenOccupancyDuration);
387
+ // Acquire the service.
388
+ const service = acquireService(this.hap, this.accessory, this.hap.Service.OccupancySensor, this.name + " Open", RatgdoReservedNames.OCCUPANCY_SENSOR_DOOR_OPEN);
389
+ if (!service) {
390
+ this.log.error("Unable to add the door open occupancy sensor.");
391
+ return false;
392
+ }
393
+ // Initialize the occupancy sensor.
394
+ service.updateCharacteristic(this.hap.Characteristic.OccupancyDetected, false);
395
+ service.updateCharacteristic(this.hap.Characteristic.StatusActive, this.status.availability);
396
+ service.getCharacteristic(this.hap.Characteristic.StatusActive).onGet(() => this.status.availability);
397
+ this.log.info("Enabling the door open indicator occupancy sensor. Occupancy will be triggered when the opener has been continuously open for more than %s seconds.", this.hints.doorOpenOccupancyDuration);
455
398
  return true;
456
399
  }
457
400
  // Configure the motion occupancy sensor for HomeKit.
458
401
  configureMotionOccupancySensor() {
459
- // Find the occupancy sensor service, if it exists.
460
- let occupancyService = this.accessory.getServiceById(this.hap.Service.OccupancySensor, RatgdoReservedNames.OCCUPANCY_SENSOR_MOTION);
461
- // The occupancy sensor is disabled by default and primarily exists for automation purposes.
462
- if (!this.hints.motionOccupancySensor) {
463
- if (occupancyService) {
464
- this.accessory.removeService(occupancyService);
465
- this.log.info("Disabling occupancy sensor.");
402
+ // Validate whether we should have this service enabled.
403
+ if (!validService(this.accessory, this.hap.Service.OccupancySensor, () => {
404
+ // The occupancy sensor is disabled by default and primarily exists for automation purposes.
405
+ if (!this.hints.motionOccupancySensor) {
406
+ return false;
466
407
  }
408
+ return true;
409
+ }, RatgdoReservedNames.OCCUPANCY_SENSOR_MOTION)) {
467
410
  return false;
468
411
  }
469
- // We don't have an occupancy sensor, let's add it to the device.
470
- if (!occupancyService) {
471
- // We don't have it, add the occupancy sensor to the device.
472
- occupancyService = new this.hap.Service.OccupancySensor(this.name, RatgdoReservedNames.OCCUPANCY_SENSOR_MOTION);
473
- if (!occupancyService) {
474
- this.log.error("Unable to add occupancy sensor.");
475
- return false;
476
- }
477
- occupancyService.addOptionalCharacteristic(this.hap.Characteristic.ConfiguredName);
478
- this.setServiceName(occupancyService, this.name);
479
- this.accessory.addService(occupancyService);
480
- }
481
- // Initialize the state of the occupancy sensor.
482
- occupancyService.updateCharacteristic(this.hap.Characteristic.OccupancyDetected, false);
483
- occupancyService.updateCharacteristic(this.hap.Characteristic.StatusActive, this.status.availability);
484
- occupancyService.getCharacteristic(this.hap.Characteristic.StatusActive).onGet(() => {
485
- return this.status.availability;
486
- });
487
- this.log.info("Enabling occupancy sensor. Occupancy event duration set to %s seconds.", this.hints.motionOccupancyDuration);
412
+ // Acquire the service.
413
+ const service = acquireService(this.hap, this.accessory, this.hap.Service.OccupancySensor, this.name, RatgdoReservedNames.OCCUPANCY_SENSOR_MOTION);
414
+ if (!service) {
415
+ this.log.error("Unable to add the occupancy sensor.");
416
+ return false;
417
+ }
418
+ // Initialize the occupancy sensor.
419
+ service.updateCharacteristic(this.hap.Characteristic.OccupancyDetected, false);
420
+ service.updateCharacteristic(this.hap.Characteristic.StatusActive, this.status.availability);
421
+ service.getCharacteristic(this.hap.Characteristic.StatusActive).onGet(() => this.status.availability);
422
+ this.log.info("Enabling the occupancy sensor. Occupancy event duration set to %s seconds.", this.hints.motionOccupancyDuration);
488
423
  return true;
489
424
  }
490
425
  // Open or close the garage door.
@@ -884,14 +819,6 @@ export class RatgdoAccessory {
884
819
  hasFeature(option) {
885
820
  return this.platform.featureOptions.test(option, this.device.mac);
886
821
  }
887
- // Utility function to set the name of a service.
888
- setServiceName(service, name) {
889
- if (!service) {
890
- return;
891
- }
892
- service.displayName = name;
893
- service.updateCharacteristic(this.hap.Characteristic.ConfiguredName, name);
894
- }
895
822
  // Utility function to return the name of this device.
896
823
  get name() {
897
824
  // We use the garage door service as the natural proxy for the name.