homebridge-ratgdo 2.5.0 → 2.6.1

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.
@@ -35,8 +35,15 @@ export class RatgdoAccessory {
35
35
  };
36
36
  // Initialize our internal state.
37
37
  this.status.availability = false;
38
+ this.status.discoLaser = false;
39
+ this.status.discoLed = false;
40
+ this.status.discoBatteryState = false;
41
+ this.status.discoVehicleArriving = false;
42
+ this.status.discoVehicleLeaving = false;
43
+ this.status.discoVehiclePresence = false;
38
44
  this.status.door = this.hap.Characteristic.CurrentDoorState.CLOSED;
39
45
  this.status.doorPosition = 0;
46
+ this.status.konnectedStrobe = false;
40
47
  this.status.light = false;
41
48
  this.status.lock = this.hap.Characteristic.LockCurrentState.UNSECURED;
42
49
  this.status.motion = false;
@@ -58,24 +65,42 @@ export class RatgdoAccessory {
58
65
  this.configureMqtt();
59
66
  this.configureAutomationDoorPositionDimmer();
60
67
  this.configureAutomationDoorSwitch();
61
- this.configureAutomationKonnected();
68
+ this.configureAutomationLockoutSwitch();
62
69
  this.configureDoorOpenOccupancySensor();
63
70
  this.configureLight();
64
- this.configureAutomationLockoutSwitch();
65
71
  this.configureMotionSensor();
66
72
  this.configureMotionOccupancySensor();
73
+ // Configure Ratgdo (ESP32) Disco-specific features.
74
+ this.configureDiscoBattery();
75
+ this.configureDiscoLaserSwitch();
76
+ this.configureDiscoLedSwitch();
77
+ this.configureDiscoVehicleArrivingContactSensor();
78
+ this.configureDiscoVehicleLeavingContactSensor();
79
+ this.configureDiscoVehiclePresenceOccupancySensor();
80
+ // Configure Konnected-specific features.
81
+ this.configureKonnectedPcwSwitch();
82
+ this.configureKonnectedStrobeSwitch();
67
83
  }
68
84
  // Configure device-specific settings.
69
85
  configureHints() {
70
86
  this.hints.automationDimmer = this.hasFeature("Opener.Dimmer");
71
87
  this.hints.automationSwitch = this.hasFeature("Opener.Switch");
88
+ this.hints.discoBattery = this.hasFeature("Disco.Battery");
89
+ this.hints.discoLaserSwitch = this.hasFeature("Disco.Switch.Laser");
90
+ this.hints.discoLedSwitch = this.hasFeature("Disco.Switch.Led");
91
+ this.hints.discoVehicleArriving = this.hasFeature("Disco.ContactSensor.Vehicle.Arriving");
92
+ this.hints.discoVehicleLeaving = this.hasFeature("Disco.ContactSensor.Vehicle.Leaving");
93
+ this.hints.discoVehiclePresence = this.hasFeature("Disco.OccupancySensor.Vehicle.Presence");
72
94
  this.hints.doorOpenOccupancySensor = this.hasFeature("Opener.OccupancySensor");
73
95
  this.hints.doorOpenOccupancyDuration = this.platform.featureOptions.getInteger("Opener.OccupancySensor.Duration", this.device.mac) ?? RATGDO_OCCUPANCY_DURATION;
96
+ this.hints.konnectedPcwSwitch = this.hasFeature("Konnected.Switch.Pcw");
97
+ this.hints.konnectedStrobeSwitch = this.hasFeature("Konnected.Switch.Strobe");
74
98
  this.hints.light = this.hasFeature("Light");
75
99
  this.hints.logLight = this.hasFeature("Log.Light");
76
100
  this.hints.logMotion = this.hasFeature("Log.Motion");
77
101
  this.hints.logObstruction = this.hasFeature("Log.Obstruction");
78
102
  this.hints.logOpener = this.hasFeature("Log.Opener");
103
+ this.hints.logVehiclePresence = this.hasFeature("Log.VehiclePresence");
79
104
  this.hints.lockoutSwitch = this.hasFeature("Opener.Switch.RemoteLockout");
80
105
  this.hints.motionOccupancySensor = this.hasFeature("Motion.OccupancySensor");
81
106
  this.hints.motionOccupancyDuration = this.platform.featureOptions.getInteger("Motion.OccupancySensor.Duration", this.device.mac) ?? RATGDO_OCCUPANCY_DURATION;
@@ -336,12 +361,166 @@ export class RatgdoAccessory {
336
361
  this.log.info("Enabling the automation door opener switch.");
337
362
  return true;
338
363
  }
339
- // Configure Konnected-specific buttons for automation purposes.
340
- configureAutomationKonnected() {
364
+ // Configure the Ratgdo (ESP32) Disco-specific backup battery service.
365
+ configureDiscoBattery() {
366
+ // Validate whether we should have this service enabled.
367
+ if (!validService(this.accessory, this.hap.Service.Battery, () => {
368
+ // We only enable this on Ratgdo devices when the user has enabled this capability.
369
+ if ((this.device.variant !== RatgdoVariant.RATGDO) || !this.hints.discoBattery) {
370
+ return false;
371
+ }
372
+ return true;
373
+ })) {
374
+ return false;
375
+ }
376
+ // Acquire the service.
377
+ const service = acquireService(this.hap, this.accessory, this.hap.Service.Battery, this.name);
378
+ if (!service) {
379
+ this.log.error("Unable to add the Ratgdo (ESP32) Disco backup battery status.");
380
+ return false;
381
+ }
382
+ // Return the current state of the charging state.
383
+ service.getCharacteristic(this.hap.Characteristic.ChargingState)?.onGet(() => this.status.discoBatteryState);
384
+ // Initialize the charging state.
385
+ service.updateCharacteristic(this.hap.Characteristic.ChargingState, this.status.discoBatteryState);
386
+ this.log.info("Enabling the Ratgdo (ESP32) Disco backup battery status.");
387
+ return true;
388
+ }
389
+ // Configure the Ratgdo (ESP32) Disco-specific parking assistance laser switch.
390
+ configureDiscoLaserSwitch() {
341
391
  // Validate whether we should have this service enabled.
342
392
  if (!validService(this.accessory, this.hap.Service.Switch, () => {
343
- // We only enable this on Konnected devices when the user has enabled automation capabilities.
344
- if ((this.device.variant !== RatgdoVariant.KONNECTED) || (!this.hints.automationDimmer && !this.hints.automationSwitch)) {
393
+ // We only enable this on Ratgdo devices when the user has enabled this capability.
394
+ if ((this.device.variant !== RatgdoVariant.RATGDO) || !this.hints.discoLaserSwitch) {
395
+ return false;
396
+ }
397
+ return true;
398
+ }, RatgdoReservedNames.SWITCH_DISCO_LASER)) {
399
+ return false;
400
+ }
401
+ // Acquire the service.
402
+ const service = acquireService(this.hap, this.accessory, this.hap.Service.Switch, this.name + " Laser", RatgdoReservedNames.SWITCH_DISCO_LASER);
403
+ if (!service) {
404
+ this.log.error("Unable to add the Ratgdo (ESP32) Disco laser switch.");
405
+ return false;
406
+ }
407
+ // Return the current state of the switch.
408
+ service.getCharacteristic(this.hap.Characteristic.On)?.onGet(() => this.status.discoLaser);
409
+ // Open or close the switch.
410
+ service.getCharacteristic(this.hap.Characteristic.On)?.onSet((value) => void this.command("disco-laser", value === true ? "on" : "off"));
411
+ // Initialize the switch.
412
+ service.updateCharacteristic(this.hap.Characteristic.On, this.status.discoLaser);
413
+ this.log.info("Enabling the Ratgdo (ESP32) Disco parking assistance laser switch.");
414
+ return true;
415
+ }
416
+ // Configure the Ratgdo (ESP32) Disco-specific LED switch.
417
+ configureDiscoLedSwitch() {
418
+ // Validate whether we should have this service enabled.
419
+ if (!validService(this.accessory, this.hap.Service.Switch, () => {
420
+ // We only enable this on Ratgdo devices when the user has enabled this capability.
421
+ if ((this.device.variant !== RatgdoVariant.RATGDO) || !this.hints.discoLedSwitch) {
422
+ return false;
423
+ }
424
+ return true;
425
+ }, RatgdoReservedNames.SWITCH_DISCO_LED)) {
426
+ return false;
427
+ }
428
+ // Acquire the service.
429
+ const service = acquireService(this.hap, this.accessory, this.hap.Service.Switch, this.name + " LED", RatgdoReservedNames.SWITCH_DISCO_LED);
430
+ if (!service) {
431
+ this.log.error("Unable to add the Ratgdo (ESP32) Disco LED switch.");
432
+ return false;
433
+ }
434
+ // Return the current state of the switch.
435
+ service.getCharacteristic(this.hap.Characteristic.On)?.onGet(() => this.status.discoLed);
436
+ // Open or close the switch.
437
+ service.getCharacteristic(this.hap.Characteristic.On)?.onSet((value) => void this.command("disco-led", value === true ? "on" : "off"));
438
+ // Initialize the switch.
439
+ service.updateCharacteristic(this.hap.Characteristic.On, this.status.discoLed);
440
+ this.log.info("Enabling the Ratgdo (ESP32) Disco LED switch.");
441
+ return true;
442
+ }
443
+ // Configure the vehicle arriving contact sensor for HomeKit.
444
+ configureDiscoVehicleArrivingContactSensor() {
445
+ // Validate whether we should have this service enabled.
446
+ if (!validService(this.accessory, this.hap.Service.ContactSensor, () => {
447
+ // We only enable this on Ratgdo devices when the user has enabled this capability.
448
+ if ((this.device.variant !== RatgdoVariant.RATGDO) || !this.hints.discoVehicleArriving) {
449
+ return false;
450
+ }
451
+ return true;
452
+ }, RatgdoReservedNames.CONTACT_DISCO_VEHICLE_ARRIVING)) {
453
+ return false;
454
+ }
455
+ // Acquire the service.
456
+ const service = acquireService(this.hap, this.accessory, this.hap.Service.ContactSensor, this.name + " Vehicle Arriving", RatgdoReservedNames.CONTACT_DISCO_VEHICLE_ARRIVING);
457
+ if (!service) {
458
+ this.log.error("Unable to add the vehicle arriving contact sensor.");
459
+ return false;
460
+ }
461
+ // Initialize the occupancy sensor.
462
+ service.updateCharacteristic(this.hap.Characteristic.ContactSensorState, false);
463
+ service.updateCharacteristic(this.hap.Characteristic.StatusActive, this.status.availability);
464
+ service.getCharacteristic(this.hap.Characteristic.StatusActive).onGet(() => this.status.availability);
465
+ this.log.info("Enabling the Ratgdo (ESP32) Disco vehicle arriving contact sensor.");
466
+ return true;
467
+ }
468
+ // Configure the vehicle leaving contact sensor for HomeKit.
469
+ configureDiscoVehicleLeavingContactSensor() {
470
+ // Validate whether we should have this service enabled.
471
+ if (!validService(this.accessory, this.hap.Service.ContactSensor, () => {
472
+ // We only enable this on Ratgdo devices when the user has enabled this capability.
473
+ if ((this.device.variant !== RatgdoVariant.RATGDO) || !this.hints.discoVehicleLeaving) {
474
+ return false;
475
+ }
476
+ return true;
477
+ }, RatgdoReservedNames.CONTACT_DISCO_VEHICLE_LEAVING)) {
478
+ return false;
479
+ }
480
+ // Acquire the service.
481
+ const service = acquireService(this.hap, this.accessory, this.hap.Service.ContactSensor, this.name + " Vehicle Leaving", RatgdoReservedNames.CONTACT_DISCO_VEHICLE_LEAVING);
482
+ if (!service) {
483
+ this.log.error("Unable to add the vehicle leaving contact sensor.");
484
+ return false;
485
+ }
486
+ // Initialize the occupancy sensor.
487
+ service.updateCharacteristic(this.hap.Characteristic.ContactSensorState, false);
488
+ service.updateCharacteristic(this.hap.Characteristic.StatusActive, this.status.availability);
489
+ service.getCharacteristic(this.hap.Characteristic.StatusActive).onGet(() => this.status.availability);
490
+ this.log.info("Enabling the Ratgdo (ESP32) Disco vehicle leaving contact sensor.");
491
+ return true;
492
+ }
493
+ // Configure the vehicle presence occupancy sensor for HomeKit.
494
+ configureDiscoVehiclePresenceOccupancySensor() {
495
+ // Validate whether we should have this service enabled.
496
+ if (!validService(this.accessory, this.hap.Service.OccupancySensor, () => {
497
+ // We only enable this on Ratgdo devices when the user has enabled this capability.
498
+ if ((this.device.variant !== RatgdoVariant.RATGDO) || !this.hints.discoVehiclePresence) {
499
+ return false;
500
+ }
501
+ return true;
502
+ }, RatgdoReservedNames.OCCUPANCY_DISCO_VEHICLE_PRESENCE)) {
503
+ return false;
504
+ }
505
+ // Acquire the service.
506
+ const service = acquireService(this.hap, this.accessory, this.hap.Service.OccupancySensor, this.name + " Vehicle Presence", RatgdoReservedNames.OCCUPANCY_DISCO_VEHICLE_PRESENCE);
507
+ if (!service) {
508
+ this.log.error("Unable to add the vehicle presence occupancy sensor.");
509
+ return false;
510
+ }
511
+ // Initialize the occupancy sensor.
512
+ service.updateCharacteristic(this.hap.Characteristic.OccupancyDetected, false);
513
+ service.updateCharacteristic(this.hap.Characteristic.StatusActive, this.status.availability);
514
+ service.getCharacteristic(this.hap.Characteristic.StatusActive).onGet(() => this.status.availability);
515
+ this.log.info("Enabling the Ratgdo (ESP32) Disco vehicle presence occupancy sensor.");
516
+ return true;
517
+ }
518
+ // Configure the Konnected-specific pre-close warning switch.
519
+ configureKonnectedPcwSwitch() {
520
+ // Validate whether we should have this service enabled.
521
+ if (!validService(this.accessory, this.hap.Service.Switch, () => {
522
+ // We only enable this on Konnected devices when the user has enabled this capability.
523
+ if ((this.device.variant !== RatgdoVariant.KONNECTED) || !this.hints.konnectedPcwSwitch) {
345
524
  return false;
346
525
  }
347
526
  return true;
@@ -351,25 +530,53 @@ export class RatgdoAccessory {
351
530
  // Acquire the service.
352
531
  const service = acquireService(this.hap, this.accessory, this.hap.Service.Switch, this.name + " Pre Close Warning", RatgdoReservedNames.SWITCH_KONNECTED_PCW);
353
532
  if (!service) {
354
- this.log.error("Unable to add the Konnected pre-close warning automation switch.");
533
+ this.log.error("Unable to add the Konnected pre-close warning switch.");
355
534
  return false;
356
535
  }
357
536
  // Return the current state of the switch.
358
537
  service.getCharacteristic(this.hap.Characteristic.On)?.onGet(() => false);
359
538
  // Open or close the switch.
360
539
  service.getCharacteristic(this.hap.Characteristic.On)?.onSet(async (value) => {
540
+ // Default to reseting our switch state after the pre-close warning has completed playing.
541
+ let resetTimer = 5000;
361
542
  // Send the command.
362
543
  if (!(await this.command("konnected-pcw"))) {
363
- // Something went wrong. Let's make sure we revert the switch to it's prior state.
364
- setTimeout(() => service?.updateCharacteristic(this.hap.Characteristic.On, !value), 50);
365
- return;
544
+ // Something went wrong. Let's make sure we revert the switch to it's prior state immediately.
545
+ resetTimer = 50;
366
546
  }
367
- // Reset the switch state after the pre-close warning is complete.
368
- setTimeout(() => service?.updateCharacteristic(this.hap.Characteristic.On, !value), 5000);
547
+ // Reset the switch state.
548
+ setTimeout(() => service?.updateCharacteristic(this.hap.Characteristic.On, !value), resetTimer);
369
549
  });
370
550
  // Initialize the switch.
371
551
  service.updateCharacteristic(this.hap.Characteristic.On, false);
372
- // Nada right now.
552
+ this.log.info("Enabling the Konnected pre-close warning switch.");
553
+ return true;
554
+ }
555
+ // Configure the Konnected-specific strobe switch.
556
+ configureKonnectedStrobeSwitch() {
557
+ // Validate whether we should have this service enabled.
558
+ if (!validService(this.accessory, this.hap.Service.Switch, () => {
559
+ // We only enable this on Konnected devices when the user has enabled this capability.
560
+ if ((this.device.variant !== RatgdoVariant.KONNECTED) || !this.hints.konnectedStrobeSwitch) {
561
+ return false;
562
+ }
563
+ return true;
564
+ }, RatgdoReservedNames.SWITCH_KONNECTED_STROBE)) {
565
+ return false;
566
+ }
567
+ // Acquire the service.
568
+ const service = acquireService(this.hap, this.accessory, this.hap.Service.Switch, this.name + " Strobe", RatgdoReservedNames.SWITCH_KONNECTED_STROBE);
569
+ if (!service) {
570
+ this.log.error("Unable to add the Konnected strobe switch.");
571
+ return false;
572
+ }
573
+ // Return the current state of the switch.
574
+ service.getCharacteristic(this.hap.Characteristic.On)?.onGet(() => this.status.konnectedStrobe);
575
+ // Open or close the switch.
576
+ service.getCharacteristic(this.hap.Characteristic.On)?.onSet((value) => void this.command("konnected-strobe", value === true ? "on" : "off"));
577
+ // Initialize the switch.
578
+ service.updateCharacteristic(this.hap.Characteristic.On, this.status.konnectedStrobe);
579
+ this.log.info("Enabling the Konnected strobe switch.");
373
580
  return true;
374
581
  }
375
582
  // Configure a switch to control the ability to lockout all wireless remotes for the garage door opener, if the feature exists.
@@ -492,39 +699,192 @@ export class RatgdoAccessory {
492
699
  return true;
493
700
  }
494
701
  // Update the state of the accessory.
495
- updateState(event, payload, position) {
702
+ updateState(event) {
496
703
  const camelCase = (text) => text.charAt(0).toUpperCase() + text.slice(1);
497
704
  const dimmerService = this.accessory.getServiceById(this.hap.Service.Lightbulb, RatgdoReservedNames.DIMMER_OPENER_AUTOMATION);
705
+ const discoBatteryService = this.accessory.getService(this.hap.Service.Battery);
706
+ const discoLaserSwitchService = this.accessory.getServiceById(this.hap.Service.Switch, RatgdoReservedNames.SWITCH_DISCO_LASER);
707
+ const discoLedSwitchService = this.accessory.getServiceById(this.hap.Service.Switch, RatgdoReservedNames.SWITCH_DISCO_LED);
708
+ const discoVehicleArrivingContactService = this.accessory.getServiceById(this.hap.Service.ContactSensor, RatgdoReservedNames.CONTACT_DISCO_VEHICLE_ARRIVING);
709
+ const discoVehicleLeavingContactService = this.accessory.getServiceById(this.hap.Service.ContactSensor, RatgdoReservedNames.CONTACT_DISCO_VEHICLE_LEAVING);
710
+ const discoVehiclePresenceOccupancyService = this.accessory.getServiceById(this.hap.Service.OccupancySensor, RatgdoReservedNames.OCCUPANCY_DISCO_VEHICLE_PRESENCE);
498
711
  const doorOccupancyService = this.accessory.getServiceById(this.hap.Service.OccupancySensor, RatgdoReservedNames.OCCUPANCY_SENSOR_DOOR_OPEN);
499
712
  const garageDoorService = this.accessory.getService(this.hap.Service.GarageDoorOpener);
713
+ const konnectedStrobeSwitchService = this.accessory.getServiceById(this.hap.Service.Switch, RatgdoReservedNames.SWITCH_KONNECTED_STROBE);
500
714
  const lightBulbService = this.accessory.getService(this.hap.Service.Lightbulb);
501
715
  const lockoutService = this.accessory.getServiceById(this.hap.Service.Switch, RatgdoReservedNames.SWITCH_LOCKOUT);
502
716
  const motionOccupancyService = this.accessory.getServiceById(this.hap.Service.OccupancySensor, RatgdoReservedNames.OCCUPANCY_SENSOR_MOTION);
503
717
  const motionService = this.accessory.getService(this.hap.Service.MotionSensor);
504
718
  const switchService = this.accessory.getServiceById(this.hap.Service.Switch, RatgdoReservedNames.SWITCH_OPENER_AUTOMATION);
505
- switch (event) {
719
+ switch (event.id) {
506
720
  case "availability":
507
- this.status.availability = payload === "online";
508
721
  // Update our availability.
509
- doorOccupancyService?.updateCharacteristic(this.hap.Characteristic.StatusActive, this.status.availability);
510
- motionOccupancyService?.updateCharacteristic(this.hap.Characteristic.StatusActive, this.status.availability);
511
- motionService?.updateCharacteristic(this.hap.Characteristic.StatusActive, this.status.availability);
512
- // Inform the user:
513
- this.log.info("Ratgdo %s.", this.status.availability ? "connected" : "disconnected");
722
+ discoVehicleArrivingContactService?.updateCharacteristic(this.hap.Characteristic.StatusActive, event.state === "online");
723
+ discoVehicleLeavingContactService?.updateCharacteristic(this.hap.Characteristic.StatusActive, event.state === "online");
724
+ discoVehiclePresenceOccupancyService?.updateCharacteristic(this.hap.Characteristic.StatusActive, event.state === "online");
725
+ doorOccupancyService?.updateCharacteristic(this.hap.Characteristic.StatusActive, event.state === "online");
726
+ motionOccupancyService?.updateCharacteristic(this.hap.Characteristic.StatusActive, event.state === "online");
727
+ motionService?.updateCharacteristic(this.hap.Characteristic.StatusActive, event.state === "online");
728
+ // Inform the user if our availability state has changed.
729
+ if (this.status.availability !== (event.state === "online")) {
730
+ this.status.availability = event.state === "online";
731
+ this.log.info("Ratgdo %s.", this.status.availability ? "connected" : "disconnected");
732
+ }
514
733
  break;
515
- case "door":
734
+ case "battery":
735
+ switch (event.state) {
736
+ case "CHARGING":
737
+ this.status.discoBatteryState = this.hap.Characteristic.ChargingState.CHARGING;
738
+ break;
739
+ case "FULL":
740
+ this.status.discoBatteryState = this.hap.Characteristic.ChargingState.NOT_CHARGING;
741
+ break;
742
+ default:
743
+ this.log.error("Unknown battery state received: %s", event.state);
744
+ return;
745
+ }
746
+ discoBatteryService?.updateCharacteristic(this.hap.Characteristic.ChargingState, this.status.discoBatteryState);
747
+ break;
748
+ case "binary_sensor-motion":
749
+ // We only want motion detected events. We timeout the motion event on our own to allow for automations and a more holistic user experience.
750
+ if (event.state !== "ON") {
751
+ break;
752
+ }
753
+ this.status.motion = true;
754
+ // Update the motion sensor state.
755
+ motionService?.updateCharacteristic(this.hap.Characteristic.MotionDetected, this.status.motion);
756
+ // If we already have an inflight motion sensor timer, clear it out since we're restarting the timer. Also, if it's our first time detecting motion for this event
757
+ // cycle, let the user know.
758
+ if (this.motionTimer) {
759
+ clearTimeout(this.motionTimer);
760
+ }
761
+ else {
762
+ if (this.hints.logMotion) {
763
+ this.log.info("Motion detected.");
764
+ }
765
+ // Publish to MQTT, if the user has configured it.
766
+ this.platform.mqtt?.publish(this.device.mac, "motion", this.status.motion.toString());
767
+ }
768
+ // Set a timer for the motion event.
769
+ this.motionTimer = setTimeout(() => {
770
+ this.status.motion = false;
771
+ motionService?.updateCharacteristic(this.hap.Characteristic.MotionDetected, this.status.motion);
772
+ // Publish to MQTT, if the user has configured it.
773
+ this.platform.mqtt?.publish(this.device.mac, "motion", this.status.motion.toString());
774
+ }, RATGDO_MOTION_DURATION * 1000);
775
+ // If we don't have occupancy sensor support configured, we're done.
776
+ if (!this.hints.motionOccupancySensor) {
777
+ break;
778
+ }
779
+ // Kill any inflight occupancy sensor.
780
+ if (this.motionOccupancyTimer) {
781
+ clearTimeout(this.motionOccupancyTimer);
782
+ this.motionOccupancyTimer = null;
783
+ }
784
+ // If the motion occupancy sensor isn't already triggered, let's do so now.
785
+ if (motionOccupancyService?.getCharacteristic(this.hap.Characteristic.OccupancyDetected).value !== true) {
786
+ // Trigger the occupancy event in HomeKit.
787
+ motionOccupancyService?.updateCharacteristic(this.hap.Characteristic.OccupancyDetected, true);
788
+ // Publish to MQTT, if the user has configured it.
789
+ this.platform.mqtt?.publish(this.device.mac, "occupancy", "true");
790
+ // Log the event.
791
+ if (this.hints.logMotion) {
792
+ this.log.info("Occupancy detected.");
793
+ }
794
+ }
795
+ // Reset our occupancy state after occupancyDuration.
796
+ this.motionOccupancyTimer = setTimeout(() => {
797
+ // Reset the occupancy sensor.
798
+ motionOccupancyService?.updateCharacteristic(this.hap.Characteristic.OccupancyDetected, false);
799
+ // Publish to MQTT, if the user has configured it.
800
+ this.platform.mqtt?.publish(this.device.mac, "occupancy", "false");
801
+ // Log the event.
802
+ if (this.hints.logMotion) {
803
+ this.log.info("Occupancy no longer detected.");
804
+ }
805
+ // Delete the timer.
806
+ this.motionOccupancyTimer = null;
807
+ }, this.hints.motionOccupancyDuration * 1000);
808
+ break;
809
+ case "binary_sensor-obstruction":
810
+ garageDoorService?.updateCharacteristic(this.hap.Characteristic.ObstructionDetected, event.state === "ON");
811
+ // Only act if we're not already at the state we're updating to.
812
+ if (this.status.obstruction !== (event.state === "ON")) {
813
+ this.status.obstruction = event.state === "ON";
814
+ if (this.hints.logObstruction) {
815
+ this.log.info("Obstruction %sdetected.", this.status.obstruction ? "" : "no longer ");
816
+ }
817
+ // Publish to MQTT, if the user has configured it.
818
+ this.platform.mqtt?.publish(this.device.mac, "obstruction", this.status.obstruction.toString());
819
+ }
820
+ break;
821
+ case "binary_sensor-vehicle_arriving":
822
+ discoVehicleArrivingContactService?.updateCharacteristic(this.hap.Characteristic.ContactSensorState, event.state === "ON");
823
+ // Only act if we're not already at the state we're updating to.
824
+ if (this.status.discoVehicleArriving !== (event.state === "ON")) {
825
+ this.status.discoVehicleArriving = event.state === "ON";
826
+ if (this.hints.logVehiclePresence) {
827
+ this.log.info("Vehicle arriving %sdetected.", this.status.discoVehicleArriving ? "" : "no longer ");
828
+ }
829
+ // Publish to MQTT, if the user has configured it.
830
+ this.platform.mqtt?.publish(this.device.mac, "vehiclearriving", this.status.discoVehicleArriving.toString());
831
+ }
832
+ break;
833
+ case "binary_sensor-vehicle_detected":
834
+ discoVehiclePresenceOccupancyService?.updateCharacteristic(this.hap.Characteristic.OccupancyDetected, event.state === "ON");
835
+ // Only act if we're not already at the state we're updating to.
836
+ if (this.status.discoVehiclePresence !== (event.state === "ON")) {
837
+ this.status.discoVehiclePresence = event.state === "ON";
838
+ if (this.hints.logVehiclePresence) {
839
+ this.log.info("Vehicle %sdetected.", this.status.discoVehiclePresence ? "" : "no longer ");
840
+ }
841
+ // Publish to MQTT, if the user has configured it.
842
+ this.platform.mqtt?.publish(this.device.mac, "vehiclepresence", this.status.discoVehiclePresence.toString());
843
+ }
844
+ break;
845
+ case "binary_sensor-vehicle_leaving":
846
+ discoVehicleLeavingContactService?.updateCharacteristic(this.hap.Characteristic.ContactSensorState, event.state === "ON");
847
+ // Only act if we're not already at the state we're updating to.
848
+ if (this.status.discoVehicleLeaving !== (event.state === "ON")) {
849
+ this.status.discoVehicleLeaving = event.state === "ON";
850
+ if (this.hints.logVehiclePresence) {
851
+ this.log.info("Vehicle leaving %sdetected.", this.status.discoVehicleLeaving ? "" : "no longer ");
852
+ }
853
+ // Publish to MQTT, if the user has configured it.
854
+ this.platform.mqtt?.publish(this.device.mac, "vehicleleaving", this.status.discoVehicleLeaving.toString());
855
+ }
856
+ break;
857
+ case "cover-door":
858
+ case "cover-garage_door":
859
+ // Determine what action the opener is currently executing.
860
+ switch (event.current_operation) {
861
+ case "CLOSING":
862
+ case "OPENING":
863
+ // eslint-disable-next-line camelcase
864
+ event.current_operation = event.current_operation.toLowerCase();
865
+ break;
866
+ case "IDLE":
867
+ // We're in a stopped rather than open state if the door is in a position greater than 0.
868
+ // eslint-disable-next-line camelcase
869
+ event.current_operation = ((event.state === "OPEN") && (event.position !== undefined) && (event.position > 0) && (event.position < 1)) ? "stopped" :
870
+ event.state.toLowerCase();
871
+ break;
872
+ default:
873
+ this.log.error("Unknown door operation detected: %s.", event.current_operation);
874
+ return;
875
+ }
516
876
  // Update our door position automation dimmer.
517
- if (position !== undefined) {
518
- this.status.doorPosition = position;
877
+ if (event.position !== undefined) {
878
+ this.status.doorPosition = event.position * 100;
519
879
  dimmerService?.updateCharacteristic(this.hap.Characteristic.Brightness, this.status.doorPosition);
520
880
  dimmerService?.updateCharacteristic(this.hap.Characteristic.On, this.status.doorPosition > 0);
521
881
  this.log.debug("Door state: %s% open.", this.status.doorPosition.toFixed(0));
522
882
  }
523
883
  // If we're already in the state we're updating to, we're done.
524
- if (this.translateCurrentDoorState(this.status.door) === payload) {
884
+ if (this.translateCurrentDoorState(this.status.door) === event.current_operation) {
525
885
  break;
526
886
  }
527
- switch (payload) {
887
+ switch (event.current_operation) {
528
888
  case "closed":
529
889
  this.status.door = this.hap.Characteristic.CurrentDoorState.CLOSED;
530
890
  break;
@@ -565,10 +925,10 @@ export class RatgdoAccessory {
565
925
  switchService?.updateCharacteristic(this.hap.Characteristic.On, this.doorTargetStateBias(this.status.door) === this.hap.Characteristic.TargetDoorState.OPEN);
566
926
  // Inform the user:
567
927
  if (this.hints.logOpener) {
568
- this.log.info("%s.", camelCase(payload));
928
+ this.log.info("%s.", camelCase(this.translateCurrentDoorState(this.status.door)));
569
929
  }
570
930
  // If we have an open occupancy sensor configured and our door state is anything other than open, clear our occupancy state.
571
- if (this.hints.doorOpenOccupancySensor && this.doorOccupancyTimer && (payload !== "open")) {
931
+ if (this.hints.doorOpenOccupancySensor && this.doorOccupancyTimer && (this.status.door !== this.hap.Characteristic.CurrentDoorState.OPEN)) {
572
932
  clearTimeout(this.doorOccupancyTimer);
573
933
  this.doorOccupancyTimer = null;
574
934
  if (doorOccupancyService?.getCharacteristic(this.hap.Characteristic.OccupancyDetected).value) {
@@ -581,109 +941,67 @@ export class RatgdoAccessory {
581
941
  }
582
942
  }
583
943
  // Publish to MQTT, if the user has configured it.
584
- this.platform.mqtt?.publish(this.device.mac, "garagedoor", payload);
944
+ this.platform.mqtt?.publish(this.device.mac, "garagedoor", this.translateCurrentDoorState(this.status.door));
585
945
  break;
586
- case "light":
946
+ case "light-garage_light":
947
+ case "light-light":
587
948
  // Only act if we're not already at the state we're updating to.
588
- if (this.status.light !== (payload === "on")) {
589
- this.status.light = payload === "on";
949
+ if (this.status.light !== (event.state === "ON")) {
950
+ this.status.light = event.state === "ON";
590
951
  lightBulbService?.updateCharacteristic(this.hap.Characteristic.On, this.status.light);
591
952
  // Inform the user:
592
953
  if (this.hints.logLight) {
593
- this.log.info("Light %s.", payload);
954
+ this.log.info("Light %s.", event.state.toLowerCase());
594
955
  }
595
956
  // Publish to MQTT, if the user has configured it.
596
957
  this.platform.mqtt?.publish(this.device.mac, "light", this.status.light.toString());
597
958
  }
598
959
  break;
599
- case "lock":
960
+ case "lock-lock_remotes":
600
961
  // If we're already in the state we're updating to, we're done.
601
- if (this.status.lock === (payload === "locked" ? this.hap.Characteristic.LockCurrentState.SECURED : this.hap.Characteristic.LockCurrentState.UNSECURED)) {
962
+ if (this.status.lock === (event.state === "LOCKED" ? this.hap.Characteristic.LockCurrentState.SECURED : this.hap.Characteristic.LockCurrentState.UNSECURED)) {
602
963
  break;
603
964
  }
604
965
  // Determine our lock state.
605
- this.status.lock = payload === "locked" ? this.hap.Characteristic.LockCurrentState.SECURED : this.hap.Characteristic.LockCurrentState.UNSECURED;
966
+ this.status.lock = (event.state === "LOCKED") ? this.hap.Characteristic.LockCurrentState.SECURED : this.hap.Characteristic.LockCurrentState.UNSECURED;
606
967
  // Update our lock state.
607
- garageDoorService?.updateCharacteristic(this.hap.Characteristic.LockTargetState, payload === "locked" ?
968
+ garageDoorService?.updateCharacteristic(this.hap.Characteristic.LockTargetState, (event.state === "LOCKED") ?
608
969
  this.hap.Characteristic.LockTargetState.SECURED : this.hap.Characteristic.LockTargetState.UNSECURED);
609
970
  garageDoorService?.updateCharacteristic(this.hap.Characteristic.LockCurrentState, this.status.lock);
610
971
  lockoutService?.updateCharacteristic(this.hap.Characteristic.On, this.status.lock === this.hap.Characteristic.LockCurrentState.SECURED);
611
972
  // Inform the user:
612
- this.log.info("Wireless remotes are %s.", payload === "locked" ? "locked out" : "permitted");
973
+ this.log.info("Wireless remotes are %s.", (event.state === "LOCKED") ? "locked out" : "permitted");
613
974
  // Publish to MQTT, if the user has configured it.
614
975
  this.platform.mqtt?.publish(this.device.mac, "lock", this.status.lock.toString());
615
976
  break;
616
- case "motion":
617
- // We only want motion detected events. We timeout the motion event on our own to allow for automations and a more holistic user experience.
618
- if (payload !== "detected") {
619
- break;
620
- }
621
- this.status.motion = true;
622
- // Update the motion sensor state.
623
- motionService?.updateCharacteristic(this.hap.Characteristic.MotionDetected, this.status.motion);
624
- // If we already have an inflight motion sensor timer, clear it out since we're restarting the timer. Also, if it's our first time detecting motion for this event
625
- // cycle, let the user know.
626
- if (this.motionTimer) {
627
- clearTimeout(this.motionTimer);
628
- }
629
- else {
630
- if (this.hints.logMotion) {
631
- this.log.info("Motion detected.");
632
- }
633
- // Publish to MQTT, if the user has configured it.
634
- this.platform.mqtt?.publish(this.device.mac, "motion", this.status.motion.toString());
635
- }
636
- // Set a timer for the motion event.
637
- this.motionTimer = setTimeout(() => {
638
- this.status.motion = false;
639
- motionService?.updateCharacteristic(this.hap.Characteristic.MotionDetected, this.status.motion);
977
+ case "sensor-voltage":
978
+ //ratgdoAccessory.updateState("voltage", event.state);
979
+ break;
980
+ case "switch-laser":
981
+ // Only act if we're not already at the state we're updating to.
982
+ if (this.status.discoLaser !== (event.state === "ON")) {
983
+ this.status.discoLaser = event.state === "ON";
984
+ discoLaserSwitchService?.updateCharacteristic(this.hap.Characteristic.On, this.status.discoLaser);
640
985
  // Publish to MQTT, if the user has configured it.
641
- this.platform.mqtt?.publish(this.device.mac, "motion", this.status.motion.toString());
642
- }, RATGDO_MOTION_DURATION * 1000);
643
- // If we don't have occupancy sensor support configured, we're done.
644
- if (!this.hints.motionOccupancySensor) {
645
- break;
646
- }
647
- // Kill any inflight occupancy sensor.
648
- if (this.motionOccupancyTimer) {
649
- clearTimeout(this.motionOccupancyTimer);
650
- this.motionOccupancyTimer = null;
986
+ this.platform.mqtt?.publish(this.device.mac, "laser", this.status.discoLaser.toString());
651
987
  }
652
- // If the motion occupancy sensor isn't already triggered, let's do so now.
653
- if (motionOccupancyService?.getCharacteristic(this.hap.Characteristic.OccupancyDetected).value !== true) {
654
- // Trigger the occupancy event in HomeKit.
655
- motionOccupancyService?.updateCharacteristic(this.hap.Characteristic.OccupancyDetected, true);
988
+ break;
989
+ case "switch-led":
990
+ // Only act if we're not already at the state we're updating to.
991
+ if (this.status.discoLed !== (event.state === "ON")) {
992
+ this.status.discoLed = event.state === "ON";
993
+ discoLedSwitchService?.updateCharacteristic(this.hap.Characteristic.On, this.status.discoLed);
656
994
  // Publish to MQTT, if the user has configured it.
657
- this.platform.mqtt?.publish(this.device.mac, "occupancy", "true");
658
- // Log the event.
659
- if (this.hints.logMotion) {
660
- this.log.info("Occupancy detected.");
661
- }
995
+ this.platform.mqtt?.publish(this.device.mac, "led", this.status.discoLed.toString());
662
996
  }
663
- // Reset our occupancy state after occupancyDuration.
664
- this.motionOccupancyTimer = setTimeout(() => {
665
- // Reset the occupancy sensor.
666
- motionOccupancyService?.updateCharacteristic(this.hap.Characteristic.OccupancyDetected, false);
667
- // Publish to MQTT, if the user has configured it.
668
- this.platform.mqtt?.publish(this.device.mac, "occupancy", "false");
669
- // Log the event.
670
- if (this.hints.logMotion) {
671
- this.log.info("Occupancy no longer detected.");
672
- }
673
- // Delete the timer.
674
- this.motionOccupancyTimer = null;
675
- }, this.hints.motionOccupancyDuration * 1000);
676
997
  break;
677
- case "obstruction":
678
- garageDoorService?.updateCharacteristic(this.hap.Characteristic.ObstructionDetected, payload === "obstructed");
998
+ case "switch-str_output":
679
999
  // Only act if we're not already at the state we're updating to.
680
- if (this.status.obstruction !== (payload === "obstructed")) {
681
- this.status.obstruction = payload === "obstructed";
682
- if (this.hints.logObstruction) {
683
- this.log.info("Obstruction %sdetected.", this.status.obstruction ? "" : "no longer ");
684
- }
1000
+ if (this.status.konnectedStrobe !== (event.state === "ON")) {
1001
+ this.status.konnectedStrobe = event.state === "ON";
1002
+ konnectedStrobeSwitchService?.updateCharacteristic(this.hap.Characteristic.On, this.status.konnectedStrobe);
685
1003
  // Publish to MQTT, if the user has configured it.
686
- this.platform.mqtt?.publish(this.device.mac, "obstruction", this.status.obstruction.toString());
1004
+ this.platform.mqtt?.publish(this.device.mac, "strobe", this.status.konnectedStrobe.toString());
687
1005
  }
688
1006
  break;
689
1007
  case "default":
@@ -696,6 +1014,14 @@ export class RatgdoAccessory {
696
1014
  let endpoint;
697
1015
  let action;
698
1016
  switch (topic) {
1017
+ case "disco-laser":
1018
+ endpoint = "switch/laser";
1019
+ action = (payload === "on") ? "turn_on" : "turn_off";
1020
+ break;
1021
+ case "disco-led":
1022
+ endpoint = "switch/led";
1023
+ action = (payload === "on") ? "turn_on" : "turn_off";
1024
+ break;
699
1025
  case "door":
700
1026
  endpoint = (this.device.variant === RatgdoVariant.KONNECTED) ? "cover/garage_door" : "cover/door";
701
1027
  switch (payload) {
@@ -722,6 +1048,10 @@ export class RatgdoAccessory {
722
1048
  endpoint = "button/pre-close_warning";
723
1049
  action = "press";
724
1050
  break;
1051
+ case "konnected-strobe":
1052
+ endpoint = "switch/str_output";
1053
+ action = (payload === "on") ? "turn_on" : "turn_off";
1054
+ break;
725
1055
  case "light":
726
1056
  endpoint = (this.device.variant === RatgdoVariant.KONNECTED) ? "light/garage_light" : "light/light";
727
1057
  action = (payload === "on") ? "turn_on" : "turn_off";