homebridge-ratgdo 2.4.0 → 2.6.0

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,23 +65,42 @@ export class RatgdoAccessory {
58
65
  this.configureMqtt();
59
66
  this.configureAutomationDoorPositionDimmer();
60
67
  this.configureAutomationDoorSwitch();
68
+ this.configureAutomationLockoutSwitch();
61
69
  this.configureDoorOpenOccupancySensor();
62
70
  this.configureLight();
63
- this.configureAutomationLockoutSwitch();
64
71
  this.configureMotionSensor();
65
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();
66
83
  }
67
84
  // Configure device-specific settings.
68
85
  configureHints() {
69
86
  this.hints.automationDimmer = this.hasFeature("Opener.Dimmer");
70
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");
71
94
  this.hints.doorOpenOccupancySensor = this.hasFeature("Opener.OccupancySensor");
72
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");
73
98
  this.hints.light = this.hasFeature("Light");
74
99
  this.hints.logLight = this.hasFeature("Log.Light");
75
100
  this.hints.logMotion = this.hasFeature("Log.Motion");
76
101
  this.hints.logObstruction = this.hasFeature("Log.Obstruction");
77
102
  this.hints.logOpener = this.hasFeature("Log.Opener");
103
+ this.hints.logVehiclePresence = this.hasFeature("Log.VehiclePresence");
78
104
  this.hints.lockoutSwitch = this.hasFeature("Opener.Switch.RemoteLockout");
79
105
  this.hints.motionOccupancySensor = this.hasFeature("Motion.OccupancySensor");
80
106
  this.hints.motionOccupancyDuration = this.platform.featureOptions.getInteger("Motion.OccupancySensor.Duration", this.device.mac) ?? RATGDO_OCCUPANCY_DURATION;
@@ -90,7 +116,7 @@ export class RatgdoAccessory {
90
116
  // Update the manufacturer information for this device.
91
117
  this.accessory.getService(this.hap.Service.AccessoryInformation)?.updateCharacteristic(this.hap.Characteristic.Manufacturer, "github.com/hjdhjd");
92
118
  // Update the model information for this device.
93
- this.accessory.getService(this.hap.Service.AccessoryInformation)?.updateCharacteristic(this.hap.Characteristic.Model, "Ratgdo");
119
+ this.accessory.getService(this.hap.Service.AccessoryInformation)?.updateCharacteristic(this.hap.Characteristic.Model, (this.device.variant === RatgdoVariant.KONNECTED) ? "Konnected" : "Ratgdo");
94
120
  // Update the serial number for this device.
95
121
  this.accessory.getService(this.hap.Service.AccessoryInformation)?.updateCharacteristic(this.hap.Characteristic.SerialNumber, this.device.mac);
96
122
  // Update the firmware information for this device.
@@ -191,8 +217,6 @@ export class RatgdoAccessory {
191
217
  });
192
218
  service.updateCharacteristic(this.hap.Characteristic.LockCurrentState, this.status.lock);
193
219
  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
220
  // Let HomeKit know that this is the primary service on this accessory.
197
221
  service.setPrimaryService(true);
198
222
  return true;
@@ -337,6 +361,224 @@ export class RatgdoAccessory {
337
361
  this.log.info("Enabling the automation door opener switch.");
338
362
  return true;
339
363
  }
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() {
391
+ // Validate whether we should have this service enabled.
392
+ if (!validService(this.accessory, this.hap.Service.Switch, () => {
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) {
524
+ return false;
525
+ }
526
+ return true;
527
+ }, RatgdoReservedNames.SWITCH_KONNECTED_PCW)) {
528
+ return false;
529
+ }
530
+ // Acquire the service.
531
+ const service = acquireService(this.hap, this.accessory, this.hap.Service.Switch, this.name + " Pre Close Warning", RatgdoReservedNames.SWITCH_KONNECTED_PCW);
532
+ if (!service) {
533
+ this.log.error("Unable to add the Konnected pre-close warning switch.");
534
+ return false;
535
+ }
536
+ // Return the current state of the switch.
537
+ service.getCharacteristic(this.hap.Characteristic.On)?.onGet(() => false);
538
+ // Open or close the switch.
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;
542
+ // Send the command.
543
+ if (!(await this.command("konnected-pcw"))) {
544
+ // Something went wrong. Let's make sure we revert the switch to it's prior state immediately.
545
+ resetTimer = 50;
546
+ }
547
+ // Reset the switch state.
548
+ setTimeout(() => service?.updateCharacteristic(this.hap.Characteristic.On, !value), resetTimer);
549
+ });
550
+ // Initialize the switch.
551
+ service.updateCharacteristic(this.hap.Characteristic.On, false);
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.");
580
+ return true;
581
+ }
340
582
  // Configure a switch to control the ability to lockout all wireless remotes for the garage door opener, if the feature exists.
341
583
  configureAutomationLockoutSwitch() {
342
584
  // Validate whether we should have this service enabled.
@@ -457,40 +699,192 @@ export class RatgdoAccessory {
457
699
  return true;
458
700
  }
459
701
  // Update the state of the accessory.
460
- updateState(event, payload, position) {
702
+ updateState(event) {
461
703
  const camelCase = (text) => text.charAt(0).toUpperCase() + text.slice(1);
462
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);
463
711
  const doorOccupancyService = this.accessory.getServiceById(this.hap.Service.OccupancySensor, RatgdoReservedNames.OCCUPANCY_SENSOR_DOOR_OPEN);
464
712
  const garageDoorService = this.accessory.getService(this.hap.Service.GarageDoorOpener);
713
+ const konnectedStrobeSwitchService = this.accessory.getServiceById(this.hap.Service.Switch, RatgdoReservedNames.SWITCH_KONNECTED_STROBE);
465
714
  const lightBulbService = this.accessory.getService(this.hap.Service.Lightbulb);
466
715
  const lockoutService = this.accessory.getServiceById(this.hap.Service.Switch, RatgdoReservedNames.SWITCH_LOCKOUT);
467
716
  const motionOccupancyService = this.accessory.getServiceById(this.hap.Service.OccupancySensor, RatgdoReservedNames.OCCUPANCY_SENSOR_MOTION);
468
717
  const motionService = this.accessory.getService(this.hap.Service.MotionSensor);
469
718
  const switchService = this.accessory.getServiceById(this.hap.Service.Switch, RatgdoReservedNames.SWITCH_OPENER_AUTOMATION);
470
- switch (event) {
719
+ switch (event.id) {
471
720
  case "availability":
472
- this.status.availability = payload === "online";
473
721
  // Update our availability.
474
- garageDoorService?.updateCharacteristic(this.hap.Characteristic.StatusActive, this.status.availability);
475
- doorOccupancyService?.updateCharacteristic(this.hap.Characteristic.StatusActive, this.status.availability);
476
- motionOccupancyService?.updateCharacteristic(this.hap.Characteristic.StatusActive, this.status.availability);
477
- motionService?.updateCharacteristic(this.hap.Characteristic.StatusActive, this.status.availability);
478
- // Inform the user:
479
- 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
+ }
480
733
  break;
481
- 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
+ }
482
876
  // Update our door position automation dimmer.
483
- if (position !== undefined) {
484
- this.status.doorPosition = position;
877
+ if (event.position !== undefined) {
878
+ this.status.doorPosition = event.position * 100;
485
879
  dimmerService?.updateCharacteristic(this.hap.Characteristic.Brightness, this.status.doorPosition);
486
880
  dimmerService?.updateCharacteristic(this.hap.Characteristic.On, this.status.doorPosition > 0);
487
881
  this.log.debug("Door state: %s% open.", this.status.doorPosition.toFixed(0));
488
882
  }
489
883
  // If we're already in the state we're updating to, we're done.
490
- if (this.translateCurrentDoorState(this.status.door) === payload) {
884
+ if (this.translateCurrentDoorState(this.status.door) === event.current_operation) {
491
885
  break;
492
886
  }
493
- switch (payload) {
887
+ switch (event.current_operation) {
494
888
  case "closed":
495
889
  this.status.door = this.hap.Characteristic.CurrentDoorState.CLOSED;
496
890
  break;
@@ -531,10 +925,10 @@ export class RatgdoAccessory {
531
925
  switchService?.updateCharacteristic(this.hap.Characteristic.On, this.doorTargetStateBias(this.status.door) === this.hap.Characteristic.TargetDoorState.OPEN);
532
926
  // Inform the user:
533
927
  if (this.hints.logOpener) {
534
- this.log.info("%s.", camelCase(payload));
928
+ this.log.info("%s.", camelCase(this.translateCurrentDoorState(this.status.door)));
535
929
  }
536
930
  // If we have an open occupancy sensor configured and our door state is anything other than open, clear our occupancy state.
537
- if (this.hints.doorOpenOccupancySensor && this.doorOccupancyTimer && (payload !== "open")) {
931
+ if (this.hints.doorOpenOccupancySensor && this.doorOccupancyTimer && (this.status.door !== this.hap.Characteristic.CurrentDoorState.OPEN)) {
538
932
  clearTimeout(this.doorOccupancyTimer);
539
933
  this.doorOccupancyTimer = null;
540
934
  if (doorOccupancyService?.getCharacteristic(this.hap.Characteristic.OccupancyDetected).value) {
@@ -547,109 +941,67 @@ export class RatgdoAccessory {
547
941
  }
548
942
  }
549
943
  // Publish to MQTT, if the user has configured it.
550
- this.platform.mqtt?.publish(this.device.mac, "garagedoor", payload);
944
+ this.platform.mqtt?.publish(this.device.mac, "garagedoor", this.translateCurrentDoorState(this.status.door));
551
945
  break;
552
- case "light":
946
+ case "light-garage_light":
947
+ case "light-light":
553
948
  // Only act if we're not already at the state we're updating to.
554
- if (this.status.light !== (payload === "on")) {
555
- this.status.light = payload === "on";
949
+ if (this.status.light !== (event.state === "ON")) {
950
+ this.status.light = event.state === "ON";
556
951
  lightBulbService?.updateCharacteristic(this.hap.Characteristic.On, this.status.light);
557
952
  // Inform the user:
558
953
  if (this.hints.logLight) {
559
- this.log.info("Light %s.", payload);
954
+ this.log.info("Light %s.", event.state.toLowerCase());
560
955
  }
561
956
  // Publish to MQTT, if the user has configured it.
562
957
  this.platform.mqtt?.publish(this.device.mac, "light", this.status.light.toString());
563
958
  }
564
959
  break;
565
- case "lock":
960
+ case "lock-lock_remotes":
566
961
  // If we're already in the state we're updating to, we're done.
567
- 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)) {
568
963
  break;
569
964
  }
570
965
  // Determine our lock state.
571
- 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;
572
967
  // Update our lock state.
573
- garageDoorService?.updateCharacteristic(this.hap.Characteristic.LockTargetState, payload === "locked" ?
968
+ garageDoorService?.updateCharacteristic(this.hap.Characteristic.LockTargetState, (event.state === "LOCKED") ?
574
969
  this.hap.Characteristic.LockTargetState.SECURED : this.hap.Characteristic.LockTargetState.UNSECURED);
575
970
  garageDoorService?.updateCharacteristic(this.hap.Characteristic.LockCurrentState, this.status.lock);
576
971
  lockoutService?.updateCharacteristic(this.hap.Characteristic.On, this.status.lock === this.hap.Characteristic.LockCurrentState.SECURED);
577
972
  // Inform the user:
578
- 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");
579
974
  // Publish to MQTT, if the user has configured it.
580
975
  this.platform.mqtt?.publish(this.device.mac, "lock", this.status.lock.toString());
581
976
  break;
582
- case "motion":
583
- // We only want motion detected events. We timeout the motion event on our own to allow for automations and a more holistic user experience.
584
- if (payload !== "detected") {
585
- break;
586
- }
587
- this.status.motion = true;
588
- // Update the motion sensor state.
589
- motionService?.updateCharacteristic(this.hap.Characteristic.MotionDetected, this.status.motion);
590
- // 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
591
- // cycle, let the user know.
592
- if (this.motionTimer) {
593
- clearTimeout(this.motionTimer);
594
- }
595
- else {
596
- if (this.hints.logMotion) {
597
- this.log.info("Motion detected.");
598
- }
599
- // Publish to MQTT, if the user has configured it.
600
- this.platform.mqtt?.publish(this.device.mac, "motion", this.status.motion.toString());
601
- }
602
- // Set a timer for the motion event.
603
- this.motionTimer = setTimeout(() => {
604
- this.status.motion = false;
605
- 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);
606
985
  // Publish to MQTT, if the user has configured it.
607
- this.platform.mqtt?.publish(this.device.mac, "motion", this.status.motion.toString());
608
- }, RATGDO_MOTION_DURATION * 1000);
609
- // If we don't have occupancy sensor support configured, we're done.
610
- if (!this.hints.motionOccupancySensor) {
611
- break;
986
+ this.platform.mqtt?.publish(this.device.mac, "laser", this.status.discoLaser.toString());
612
987
  }
613
- // Kill any inflight occupancy sensor.
614
- if (this.motionOccupancyTimer) {
615
- clearTimeout(this.motionOccupancyTimer);
616
- this.motionOccupancyTimer = null;
617
- }
618
- // If the motion occupancy sensor isn't already triggered, let's do so now.
619
- if (motionOccupancyService?.getCharacteristic(this.hap.Characteristic.OccupancyDetected).value !== true) {
620
- // Trigger the occupancy event in HomeKit.
621
- 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);
622
994
  // Publish to MQTT, if the user has configured it.
623
- this.platform.mqtt?.publish(this.device.mac, "occupancy", "true");
624
- // Log the event.
625
- if (this.hints.logMotion) {
626
- this.log.info("Occupancy detected.");
627
- }
995
+ this.platform.mqtt?.publish(this.device.mac, "led", this.status.discoLed.toString());
628
996
  }
629
- // Reset our occupancy state after occupancyDuration.
630
- this.motionOccupancyTimer = setTimeout(() => {
631
- // Reset the occupancy sensor.
632
- motionOccupancyService?.updateCharacteristic(this.hap.Characteristic.OccupancyDetected, false);
633
- // Publish to MQTT, if the user has configured it.
634
- this.platform.mqtt?.publish(this.device.mac, "occupancy", "false");
635
- // Log the event.
636
- if (this.hints.logMotion) {
637
- this.log.info("Occupancy no longer detected.");
638
- }
639
- // Delete the timer.
640
- this.motionOccupancyTimer = null;
641
- }, this.hints.motionOccupancyDuration * 1000);
642
997
  break;
643
- case "obstruction":
644
- garageDoorService?.updateCharacteristic(this.hap.Characteristic.ObstructionDetected, payload === "obstructed");
998
+ case "switch-str_output":
645
999
  // Only act if we're not already at the state we're updating to.
646
- if (this.status.obstruction !== (payload === "obstructed")) {
647
- this.status.obstruction = payload === "obstructed";
648
- if (this.hints.logObstruction) {
649
- this.log.info("Obstruction %sdetected.", this.status.obstruction ? "" : "no longer ");
650
- }
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);
651
1003
  // Publish to MQTT, if the user has configured it.
652
- 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());
653
1005
  }
654
1006
  break;
655
1007
  case "default":
@@ -662,6 +1014,14 @@ export class RatgdoAccessory {
662
1014
  let endpoint;
663
1015
  let action;
664
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;
665
1025
  case "door":
666
1026
  endpoint = (this.device.variant === RatgdoVariant.KONNECTED) ? "cover/garage_door" : "cover/door";
667
1027
  switch (payload) {
@@ -684,6 +1044,14 @@ export class RatgdoAccessory {
684
1044
  return false;
685
1045
  }
686
1046
  break;
1047
+ case "konnected-pcw":
1048
+ endpoint = "button/pre-close_warning";
1049
+ action = "press";
1050
+ break;
1051
+ case "konnected-strobe":
1052
+ endpoint = "switch/str_output";
1053
+ action = (payload === "on") ? "turn_on" : "turn_off";
1054
+ break;
687
1055
  case "light":
688
1056
  endpoint = (this.device.variant === RatgdoVariant.KONNECTED) ? "light/garage_light" : "light/light";
689
1057
  action = (payload === "on") ? "turn_on" : "turn_off";
@@ -822,11 +1190,7 @@ export class RatgdoAccessory {
822
1190
  // Utility function to return the name of this device.
823
1191
  get name() {
824
1192
  // We use the garage door service as the natural proxy for the name.
825
- let name = this.accessory.getService(this.hap.Service.GarageDoorOpener)?.getCharacteristic(this.hap.Characteristic.ConfiguredName).value;
826
- if (name?.length) {
827
- return name;
828
- }
829
- name = this.accessory.getService(this.hap.Service.GarageDoorOpener)?.getCharacteristic(this.hap.Characteristic.Name).value;
1193
+ let name = this.accessory.getService(this.hap.Service.GarageDoorOpener)?.getCharacteristic(this.hap.Characteristic.Name).value;
830
1194
  if (name?.length) {
831
1195
  return name;
832
1196
  }