homebridge-ratgdo 0.2.0 → 1.1.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.
- package/LICENSE.md +1 -1
- package/README.md +7 -3
- package/config.schema.json +46 -14
- package/dist/index.d.ts +3 -0
- package/dist/index.js +5 -7
- package/dist/index.js.map +1 -1
- package/dist/ratgdo-device.d.ts +44 -0
- package/dist/ratgdo-device.js +447 -103
- package/dist/ratgdo-device.js.map +1 -1
- package/dist/ratgdo-mqtt.d.ts +19 -0
- package/dist/ratgdo-mqtt.js +155 -0
- package/dist/ratgdo-mqtt.js.map +1 -0
- package/dist/ratgdo-options.d.ts +27 -0
- package/dist/ratgdo-options.js +26 -21
- package/dist/ratgdo-options.js.map +1 -1
- package/dist/ratgdo-platform.d.ts +28 -0
- package/dist/ratgdo-platform.js +64 -30
- package/dist/ratgdo-platform.js.map +1 -1
- package/dist/ratgdo-types.d.ts +17 -0
- package/dist/ratgdo-types.js +14 -0
- package/dist/ratgdo-types.js.map +1 -0
- package/dist/settings.d.ts +8 -0
- package/dist/settings.js +14 -13
- package/dist/settings.js.map +1 -1
- package/homebridge-ui/public/index.html +113 -0
- package/homebridge-ui/public/lib/featureoptions.mjs +200 -0
- package/homebridge-ui/public/ratgdo-featureoptions.mjs +638 -0
- package/homebridge-ui/public/ui.mjs +144 -0
- package/homebridge-ui/server.js +58 -0
- package/package.json +13 -9
package/dist/ratgdo-device.js
CHANGED
|
@@ -1,13 +1,22 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
1
|
+
import { RATGDO_MOTION_DURATION, RATGDO_OCCUPANCY_DURATION, RATGDO_TRANSITION_DURATION } from "./settings.js";
|
|
2
|
+
import { RatgdoReservedNames } from "./ratgdo-types.js";
|
|
3
|
+
import { getOptionFloat, getOptionNumber, getOptionValue, isOptionEnabled } from "./ratgdo-options.js";
|
|
4
|
+
import util from "node:util";
|
|
5
|
+
export class RatgdoAccessory {
|
|
6
|
+
accessory;
|
|
7
|
+
api;
|
|
8
|
+
config;
|
|
9
|
+
device;
|
|
10
|
+
doorOccupancyTimer;
|
|
11
|
+
doorTimer;
|
|
12
|
+
hap;
|
|
13
|
+
hints;
|
|
14
|
+
log;
|
|
15
|
+
motionOccupancyTimer;
|
|
16
|
+
motionTimer;
|
|
17
|
+
obstructionTimer;
|
|
18
|
+
platform;
|
|
19
|
+
status;
|
|
11
20
|
// The constructor initializes key variables and calls configureDevice().
|
|
12
21
|
constructor(platform, accessory, device) {
|
|
13
22
|
this.accessory = accessory;
|
|
@@ -20,10 +29,10 @@ class ratgdoAccessory {
|
|
|
20
29
|
this.device = device;
|
|
21
30
|
this.platform = platform;
|
|
22
31
|
this.log = {
|
|
23
|
-
debug: (message, ...parameters) => platform.debug(
|
|
24
|
-
error: (message, ...parameters) => platform.log.error(
|
|
25
|
-
info: (message, ...parameters) => platform.log.info(
|
|
26
|
-
warn: (message, ...parameters) => platform.log.warn(
|
|
32
|
+
debug: (message, ...parameters) => platform.debug(util.format(this.name + ": " + message, ...parameters)),
|
|
33
|
+
error: (message, ...parameters) => platform.log.error(util.format(this.name + ": " + message, ...parameters)),
|
|
34
|
+
info: (message, ...parameters) => platform.log.info(util.format(this.name + ": " + message, ...parameters)),
|
|
35
|
+
warn: (message, ...parameters) => platform.log.warn(util.format(this.name + ": " + message, ...parameters))
|
|
27
36
|
};
|
|
28
37
|
// Initialize our internal state.
|
|
29
38
|
this.status.availability = false;
|
|
@@ -32,9 +41,10 @@ class ratgdoAccessory {
|
|
|
32
41
|
this.status.lock = this.hap.Characteristic.LockCurrentState.UNSECURED;
|
|
33
42
|
this.status.motion = false;
|
|
34
43
|
this.status.obstruction = false;
|
|
44
|
+
this.doorOccupancyTimer = null;
|
|
45
|
+
this.motionOccupancyTimer = null;
|
|
35
46
|
this.motionTimer = null;
|
|
36
47
|
this.obstructionTimer = null;
|
|
37
|
-
this.occupancyTimer = null;
|
|
38
48
|
this.configureDevice();
|
|
39
49
|
}
|
|
40
50
|
// Configure a garage door accessory for HomeKit.
|
|
@@ -45,40 +55,108 @@ class ratgdoAccessory {
|
|
|
45
55
|
this.configureHints();
|
|
46
56
|
this.configureInfo();
|
|
47
57
|
this.configureGarageDoor();
|
|
58
|
+
this.configureMqtt();
|
|
59
|
+
this.configureAutomationSwitch();
|
|
60
|
+
this.configureDoorOpenOccupancySensor();
|
|
48
61
|
this.configureLight();
|
|
49
62
|
this.configureMotionSensor();
|
|
50
|
-
|
|
51
|
-
// this.configureOccupancySensor();
|
|
63
|
+
this.configureMotionOccupancySensor();
|
|
52
64
|
}
|
|
53
65
|
// Configure device-specific settings.
|
|
54
66
|
configureHints() {
|
|
55
67
|
this.hints.automationSwitch = this.hasFeature("Opener.Switch");
|
|
56
|
-
this.hints.
|
|
57
|
-
|
|
68
|
+
this.hints.doorOpenOccupancySensor = this.hasFeature("Opener.OccupancySensor");
|
|
69
|
+
this.hints.doorOpenOccupancyDuration = this.getFeatureNumber("Opener.OccupancySensor.Duration") ?? RATGDO_OCCUPANCY_DURATION;
|
|
70
|
+
this.hints.light = this.hasFeature("Light");
|
|
71
|
+
this.hints.motionOccupancySensor = this.hasFeature("Motion.OccupancySensor");
|
|
72
|
+
this.hints.motionOccupancyDuration = this.getFeatureNumber("Motion.OccupancySensor.Duration") ?? RATGDO_OCCUPANCY_DURATION;
|
|
73
|
+
this.hints.motionSensor = this.hasFeature("Motion");
|
|
58
74
|
this.hints.readOnly = this.hasFeature("Opener.ReadOnly");
|
|
59
|
-
this.hints.
|
|
75
|
+
this.hints.syncName = this.hasFeature("Device.SyncName");
|
|
76
|
+
if (this.hints.readOnly) {
|
|
77
|
+
this.log.info("Garage door opener is read-only. The opener will not respond to open and close requests from HomeKit.");
|
|
78
|
+
}
|
|
79
|
+
if (this.hints.syncName) {
|
|
80
|
+
this.log.info("Syncing Ratgdo device name to HomeKit.");
|
|
81
|
+
}
|
|
60
82
|
return true;
|
|
61
83
|
}
|
|
62
84
|
// Configure the device information for HomeKit.
|
|
63
85
|
configureInfo() {
|
|
64
|
-
var _a, _b, _c, _d;
|
|
65
86
|
// Update the manufacturer information for this device.
|
|
66
|
-
|
|
87
|
+
this.accessory.getService(this.hap.Service.AccessoryInformation)?.updateCharacteristic(this.hap.Characteristic.Manufacturer, "github.com/hjdhjd");
|
|
67
88
|
// Update the model information for this device.
|
|
68
|
-
|
|
89
|
+
this.accessory.getService(this.hap.Service.AccessoryInformation)?.updateCharacteristic(this.hap.Characteristic.Model, "Ratgdo");
|
|
69
90
|
// Update the serial number for this device.
|
|
70
|
-
|
|
91
|
+
this.accessory.getService(this.hap.Service.AccessoryInformation)?.updateCharacteristic(this.hap.Characteristic.SerialNumber, this.device.mac);
|
|
71
92
|
// Update the firmware information for this device.
|
|
72
|
-
|
|
93
|
+
this.accessory.getService(this.hap.Service.AccessoryInformation)?.updateCharacteristic(this.hap.Characteristic.FirmwareRevision, this.device.firmwareVersion);
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
96
|
+
// Configure MQTT services.
|
|
97
|
+
configureMqtt() {
|
|
98
|
+
// Return our garage door state.
|
|
99
|
+
this.platform.mqtt?.subscribeGet(this, "garagedoor", "Garage Door", () => {
|
|
100
|
+
// Return our current status using our HomeKit current state decoder ring.
|
|
101
|
+
return this.translateCurrentDoorState(this.status.door);
|
|
102
|
+
});
|
|
103
|
+
// Set our garage door state.
|
|
104
|
+
this.platform.mqtt?.subscribeSet(this, "garagedoor", "Garage Door", (value) => {
|
|
105
|
+
let command;
|
|
106
|
+
switch (value) {
|
|
107
|
+
case "close":
|
|
108
|
+
command = this.hap.Characteristic.TargetDoorState.CLOSED;
|
|
109
|
+
break;
|
|
110
|
+
case "open":
|
|
111
|
+
command = this.hap.Characteristic.TargetDoorState.OPEN;
|
|
112
|
+
break;
|
|
113
|
+
default:
|
|
114
|
+
this.log.error("Invalid command.");
|
|
115
|
+
return;
|
|
116
|
+
break;
|
|
117
|
+
}
|
|
118
|
+
// Set our door state accordingly.
|
|
119
|
+
this.setDoorState(command);
|
|
120
|
+
});
|
|
121
|
+
// Return our obstruction state.
|
|
122
|
+
this.platform.mqtt?.subscribeGet(this, "obstruction", "Obstruction", () => {
|
|
123
|
+
return this.status.obstruction.toString();
|
|
124
|
+
});
|
|
125
|
+
// Return our door open occupancy state if configured to do so.
|
|
126
|
+
if (this.hints.doorOpenOccupancySensor) {
|
|
127
|
+
this.platform.mqtt?.subscribeGet(this, "dooropenoccupancy", "Door Open Indicator Occupancy", () => {
|
|
128
|
+
return (this.accessory.getServiceById(this.hap.Service.OccupancySensor, RatgdoReservedNames.OCCUPANCY_SENSOR_DOOR_OPEN)
|
|
129
|
+
?.getCharacteristic(this.hap.Characteristic.OccupancyDetected).value ?? "false").toString();
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
// Return our light state if configured to do so.
|
|
133
|
+
if (this.hints.light) {
|
|
134
|
+
this.platform.mqtt?.subscribeGet(this, "light", "Light", () => {
|
|
135
|
+
return this.status.light.toString();
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
// Return our motion occupancy state if configured to do so.
|
|
139
|
+
if (this.hints.motionOccupancySensor) {
|
|
140
|
+
this.platform.mqtt?.subscribeGet(this, "occupancy", "Occupancy", () => {
|
|
141
|
+
return (this.accessory.getServiceById(this.hap.Service.OccupancySensor, RatgdoReservedNames.OCCUPANCY_SENSOR_MOTION)
|
|
142
|
+
?.getCharacteristic(this.hap.Characteristic.OccupancyDetected).value ?? "false").toString();
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
// Return our motion state if configured to do so.
|
|
146
|
+
if (this.hints.motionSensor) {
|
|
147
|
+
this.platform.mqtt?.subscribeGet(this, "motion", "Motion", () => {
|
|
148
|
+
return this.status.motion.toString();
|
|
149
|
+
});
|
|
150
|
+
}
|
|
73
151
|
return true;
|
|
74
152
|
}
|
|
75
153
|
// Configure the garage door service for HomeKit.
|
|
76
154
|
configureGarageDoor() {
|
|
77
|
-
var _a;
|
|
78
155
|
let garageDoorService = this.accessory.getService(this.hap.Service.GarageDoorOpener);
|
|
79
156
|
// Add the garage door opener service to the accessory, if needed.
|
|
80
157
|
if (!garageDoorService) {
|
|
81
158
|
garageDoorService = new this.hap.Service.GarageDoorOpener(this.name);
|
|
159
|
+
garageDoorService.addOptionalCharacteristic(this.hap.Characteristic.ConfiguredName);
|
|
82
160
|
this.accessory.addService(garageDoorService);
|
|
83
161
|
}
|
|
84
162
|
// Set the initial current and target door states to closed since ratgdo doesn't tell us initial state over MQTT on startup.
|
|
@@ -92,23 +170,30 @@ class ratgdoAccessory {
|
|
|
92
170
|
garageDoorService.getCharacteristic(this.hap.Characteristic.CurrentDoorState).onGet(() => this.status.door);
|
|
93
171
|
// Inform HomeKit of any obstructions.
|
|
94
172
|
garageDoorService.getCharacteristic(this.hap.Characteristic.ObstructionDetected).onGet(() => this.status.obstruction === true);
|
|
95
|
-
//
|
|
96
|
-
garageDoorService.addOptionalCharacteristic(this.hap.Characteristic.LockCurrentState);
|
|
97
|
-
garageDoorService.addOptionalCharacteristic(this.hap.Characteristic.LockTargetState);
|
|
173
|
+
// Configure the lock garage door lock current and target state characteristics.
|
|
98
174
|
garageDoorService.updateCharacteristic(this.hap.Characteristic.LockCurrentState, this.status.lock);
|
|
99
175
|
garageDoorService.updateCharacteristic(this.hap.Characteristic.LockTargetState, this.lockTargetStateBias(this.status.lock));
|
|
100
|
-
// Add the configured name for this device.
|
|
101
|
-
garageDoorService.addOptionalCharacteristic(this.hap.Characteristic.ConfiguredName);
|
|
102
|
-
// switchService.updateCharacteristic(this.hap.Characteristic.ConfiguredName, switchName);
|
|
103
176
|
// Update our configured name, if requested.
|
|
104
|
-
if (this.hints.
|
|
105
|
-
|
|
106
|
-
if (this.hints.
|
|
107
|
-
(
|
|
177
|
+
if (this.hints.syncName) {
|
|
178
|
+
this.setServiceName(garageDoorService, this.device.name);
|
|
179
|
+
if (this.hints.automationSwitch) {
|
|
180
|
+
this.setServiceName(this.accessory.getServiceById(this.hap.Service.Switch, RatgdoReservedNames.SWITCH_OPENER_AUTOMATION), this.device.name + " Automation Switch");
|
|
181
|
+
}
|
|
182
|
+
if (this.hints.doorOpenOccupancySensor) {
|
|
183
|
+
this.setServiceName(this.accessory.getServiceById(this.hap.Service.OccupancySensor, RatgdoReservedNames.OCCUPANCY_SENSOR_DOOR_OPEN), this.device.name + " Open");
|
|
184
|
+
}
|
|
185
|
+
if (this.hints.light) {
|
|
186
|
+
this.setServiceName(this.accessory.getService(this.hap.Service.Lightbulb), this.device.name);
|
|
108
187
|
}
|
|
188
|
+
if (this.hints.motionSensor) {
|
|
189
|
+
this.setServiceName(this.accessory.getService(this.hap.Service.MotionSensor), this.device.name);
|
|
190
|
+
}
|
|
191
|
+
if (this.hints.motionOccupancySensor) {
|
|
192
|
+
this.setServiceName(this.accessory.getServiceById(this.hap.Service.OccupancySensor, RatgdoReservedNames.OCCUPANCY_SENSOR_MOTION), this.device.name);
|
|
193
|
+
}
|
|
194
|
+
// Finally, update the accessory name.
|
|
195
|
+
this.accessoryName = this.device.name;
|
|
109
196
|
}
|
|
110
|
-
// Add our status active characteristic.
|
|
111
|
-
garageDoorService.addOptionalCharacteristic(this.hap.Characteristic.StatusActive);
|
|
112
197
|
garageDoorService.getCharacteristic(this.hap.Characteristic.StatusActive).onGet(() => this.status.availability);
|
|
113
198
|
garageDoorService.updateCharacteristic(this.hap.Characteristic.StatusActive, this.status.availability);
|
|
114
199
|
// Let HomeKit know that this is the primary service on this accessory.
|
|
@@ -117,9 +202,16 @@ class ratgdoAccessory {
|
|
|
117
202
|
}
|
|
118
203
|
// Configure the light for HomeKit.
|
|
119
204
|
configureLight() {
|
|
120
|
-
var _a, _b;
|
|
121
205
|
// Find the service, if it exists.
|
|
122
206
|
let lightService = this.accessory.getService(this.hap.Service.Lightbulb);
|
|
207
|
+
// Have we disabled the light?
|
|
208
|
+
if (!this.hints.light) {
|
|
209
|
+
if (lightService) {
|
|
210
|
+
this.accessory.removeService(lightService);
|
|
211
|
+
this.log.info("Disabling light.");
|
|
212
|
+
}
|
|
213
|
+
return false;
|
|
214
|
+
}
|
|
123
215
|
// Add the service to the accessory, if needed.
|
|
124
216
|
if (!lightService) {
|
|
125
217
|
lightService = new this.hap.Service.Lightbulb(this.name);
|
|
@@ -127,24 +219,33 @@ class ratgdoAccessory {
|
|
|
127
219
|
this.log.error("Unable to add the light.");
|
|
128
220
|
return false;
|
|
129
221
|
}
|
|
222
|
+
lightService.addOptionalCharacteristic(this.hap.Characteristic.ConfiguredName);
|
|
223
|
+
lightService.updateCharacteristic(this.hap.Characteristic.Name, this.name);
|
|
224
|
+
this.setServiceName(lightService, this.name);
|
|
130
225
|
this.accessory.addService(lightService);
|
|
131
226
|
this.log.info("Enabling light.");
|
|
132
227
|
}
|
|
228
|
+
// Initialize the light.
|
|
229
|
+
lightService.updateCharacteristic(this.hap.Characteristic.On, this.status.light);
|
|
133
230
|
// Turn the light on or off.
|
|
134
|
-
|
|
135
|
-
|
|
231
|
+
lightService.getCharacteristic(this.hap.Characteristic.On)?.onGet(() => this.status.light);
|
|
232
|
+
lightService.getCharacteristic(this.hap.Characteristic.On)?.onSet((value) => {
|
|
136
233
|
this.command("light", value === true ? "on" : "off");
|
|
137
234
|
});
|
|
138
|
-
// Initialize the light.
|
|
139
|
-
lightService.displayName = this.name;
|
|
140
|
-
lightService.updateCharacteristic(this.hap.Characteristic.Name, this.name);
|
|
141
|
-
lightService.updateCharacteristic(this.hap.Characteristic.On, this.status.light);
|
|
142
235
|
return true;
|
|
143
236
|
}
|
|
144
237
|
// Configure the motion sensor for HomeKit.
|
|
145
238
|
configureMotionSensor() {
|
|
146
239
|
// Find the motion sensor service, if it exists.
|
|
147
240
|
let motionService = this.accessory.getService(this.hap.Service.MotionSensor);
|
|
241
|
+
// Have we disabled the motion sensor?
|
|
242
|
+
if (!this.hints.motionSensor) {
|
|
243
|
+
if (motionService) {
|
|
244
|
+
this.accessory.removeService(motionService);
|
|
245
|
+
this.log.info("Disabling motion sensor.");
|
|
246
|
+
}
|
|
247
|
+
return false;
|
|
248
|
+
}
|
|
148
249
|
// We don't have a motion sensor, let's add it to the device.
|
|
149
250
|
if (!motionService) {
|
|
150
251
|
// We don't have it, add the motion sensor to the device.
|
|
@@ -153,43 +254,154 @@ class ratgdoAccessory {
|
|
|
153
254
|
this.log.error("Unable to add the motion sensor.");
|
|
154
255
|
return false;
|
|
155
256
|
}
|
|
257
|
+
motionService.addOptionalCharacteristic(this.hap.Characteristic.ConfiguredName);
|
|
258
|
+
motionService.updateCharacteristic(this.hap.Characteristic.Name, this.name);
|
|
259
|
+
this.setServiceName(motionService, this.name);
|
|
156
260
|
this.accessory.addService(motionService);
|
|
157
261
|
this.log.info("Enabling motion sensor.");
|
|
158
262
|
}
|
|
159
263
|
// Initialize the state of the motion sensor.
|
|
160
|
-
motionService.displayName = this.name;
|
|
161
|
-
motionService.updateCharacteristic(this.hap.Characteristic.Name, this.name);
|
|
162
264
|
motionService.updateCharacteristic(this.hap.Characteristic.MotionDetected, false);
|
|
163
265
|
motionService.updateCharacteristic(this.hap.Characteristic.StatusActive, this.status.availability);
|
|
164
266
|
motionService.getCharacteristic(this.hap.Characteristic.StatusActive).onGet(() => this.status.availability);
|
|
165
267
|
return true;
|
|
166
268
|
}
|
|
269
|
+
// 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.
|
|
270
|
+
configureAutomationSwitch() {
|
|
271
|
+
// Find the switch service, if it exists.
|
|
272
|
+
let switchService = this.accessory.getServiceById(this.hap.Service.Switch, RatgdoReservedNames.SWITCH_OPENER_AUTOMATION);
|
|
273
|
+
// The switch is disabled by default and primarily exists for automation purposes.
|
|
274
|
+
if (!this.hints.automationSwitch) {
|
|
275
|
+
if (switchService) {
|
|
276
|
+
this.accessory.removeService(switchService);
|
|
277
|
+
this.log.info("Disabling automation switch.");
|
|
278
|
+
}
|
|
279
|
+
return false;
|
|
280
|
+
}
|
|
281
|
+
// Add the switch to the opener, if needed.
|
|
282
|
+
if (!switchService) {
|
|
283
|
+
switchService = new this.hap.Service.Switch(this.name + " Automation Switch", RatgdoReservedNames.SWITCH_OPENER_AUTOMATION);
|
|
284
|
+
if (!switchService) {
|
|
285
|
+
this.log.error("Unable to add automation switch.");
|
|
286
|
+
return false;
|
|
287
|
+
}
|
|
288
|
+
switchService.addOptionalCharacteristic(this.hap.Characteristic.ConfiguredName);
|
|
289
|
+
this.setServiceName(switchService, this.name + " Automation Switch");
|
|
290
|
+
this.accessory.addService(switchService);
|
|
291
|
+
}
|
|
292
|
+
// Return the current state of the opener.
|
|
293
|
+
switchService.getCharacteristic(this.hap.Characteristic.On)?.onGet(() => {
|
|
294
|
+
// We're on if we are in any state other than closed (specifically open or stopped).
|
|
295
|
+
return this.doorCurrentStateBias(this.status.door) !== this.hap.Characteristic.CurrentDoorState.CLOSED;
|
|
296
|
+
});
|
|
297
|
+
// Open or close the opener.
|
|
298
|
+
switchService.getCharacteristic(this.hap.Characteristic.On)?.onSet((value) => {
|
|
299
|
+
// Inform the user.
|
|
300
|
+
this.log.info("Automation switch: %s.", value ? "open" : "close");
|
|
301
|
+
// Send the command.
|
|
302
|
+
if (!this.setDoorState(value ? this.hap.Characteristic.TargetDoorState.OPEN : this.hap.Characteristic.TargetDoorState.CLOSED)) {
|
|
303
|
+
// Something went wrong. Let's make sure we revert the switch to it's prior state.
|
|
304
|
+
setTimeout(() => {
|
|
305
|
+
switchService?.updateCharacteristic(this.hap.Characteristic.On, !value);
|
|
306
|
+
}, 50);
|
|
307
|
+
}
|
|
308
|
+
});
|
|
309
|
+
// Initialize the switch.
|
|
310
|
+
switchService.updateCharacteristic(this.hap.Characteristic.On, this.doorCurrentStateBias(this.status.door) !== this.hap.Characteristic.CurrentDoorState.CLOSED);
|
|
311
|
+
this.log.info("Enabling automation switch.");
|
|
312
|
+
return true;
|
|
313
|
+
}
|
|
314
|
+
// Configure the door open occupancy sensor for HomeKit.
|
|
315
|
+
configureDoorOpenOccupancySensor() {
|
|
316
|
+
// Find the occupancy sensor service, if it exists.
|
|
317
|
+
let occupancyService = this.accessory.getServiceById(this.hap.Service.OccupancySensor, RatgdoReservedNames.OCCUPANCY_SENSOR_DOOR_OPEN);
|
|
318
|
+
// The occupancy sensor is disabled by default and primarily exists for automation purposes.
|
|
319
|
+
if (!this.hints.doorOpenOccupancySensor) {
|
|
320
|
+
if (occupancyService) {
|
|
321
|
+
this.accessory.removeService(occupancyService);
|
|
322
|
+
this.log.info("Disabling door open indicator occupancy sensor.");
|
|
323
|
+
}
|
|
324
|
+
return false;
|
|
325
|
+
}
|
|
326
|
+
// We don't have an occupancy sensor, let's add it to the device.
|
|
327
|
+
if (!occupancyService) {
|
|
328
|
+
// We don't have it, add the occupancy sensor to the device.
|
|
329
|
+
occupancyService = new this.hap.Service.OccupancySensor(this.name + " Open", RatgdoReservedNames.OCCUPANCY_SENSOR_DOOR_OPEN);
|
|
330
|
+
if (!occupancyService) {
|
|
331
|
+
this.log.error("Unable to add door open occupancy sensor.");
|
|
332
|
+
return false;
|
|
333
|
+
}
|
|
334
|
+
occupancyService.addOptionalCharacteristic(this.hap.Characteristic.ConfiguredName);
|
|
335
|
+
this.setServiceName(occupancyService, this.name + " Open");
|
|
336
|
+
this.accessory.addService(occupancyService);
|
|
337
|
+
}
|
|
338
|
+
// Initialize the state of the occupancy sensor.
|
|
339
|
+
occupancyService.updateCharacteristic(this.hap.Characteristic.OccupancyDetected, false);
|
|
340
|
+
occupancyService.updateCharacteristic(this.hap.Characteristic.StatusActive, this.status.availability);
|
|
341
|
+
occupancyService.getCharacteristic(this.hap.Characteristic.StatusActive).onGet(() => {
|
|
342
|
+
return this.status.availability;
|
|
343
|
+
});
|
|
344
|
+
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);
|
|
345
|
+
return true;
|
|
346
|
+
}
|
|
347
|
+
// Configure the motion occupancy sensor for HomeKit.
|
|
348
|
+
configureMotionOccupancySensor() {
|
|
349
|
+
// Find the occupancy sensor service, if it exists.
|
|
350
|
+
let occupancyService = this.accessory.getServiceById(this.hap.Service.OccupancySensor, RatgdoReservedNames.OCCUPANCY_SENSOR_MOTION);
|
|
351
|
+
// The occupancy sensor is disabled by default and primarily exists for automation purposes.
|
|
352
|
+
if (!this.hints.motionOccupancySensor) {
|
|
353
|
+
if (occupancyService) {
|
|
354
|
+
this.accessory.removeService(occupancyService);
|
|
355
|
+
this.log.info("Disabling occupancy sensor.");
|
|
356
|
+
}
|
|
357
|
+
return false;
|
|
358
|
+
}
|
|
359
|
+
// We don't have an occupancy sensor, let's add it to the device.
|
|
360
|
+
if (!occupancyService) {
|
|
361
|
+
// We don't have it, add the occupancy sensor to the device.
|
|
362
|
+
occupancyService = new this.hap.Service.OccupancySensor(this.name, RatgdoReservedNames.OCCUPANCY_SENSOR_MOTION);
|
|
363
|
+
if (!occupancyService) {
|
|
364
|
+
this.log.error("Unable to add occupancy sensor.");
|
|
365
|
+
return false;
|
|
366
|
+
}
|
|
367
|
+
occupancyService.addOptionalCharacteristic(this.hap.Characteristic.ConfiguredName);
|
|
368
|
+
this.setServiceName(occupancyService, this.name);
|
|
369
|
+
this.accessory.addService(occupancyService);
|
|
370
|
+
}
|
|
371
|
+
// Initialize the state of the occupancy sensor.
|
|
372
|
+
occupancyService.updateCharacteristic(this.hap.Characteristic.OccupancyDetected, false);
|
|
373
|
+
occupancyService.updateCharacteristic(this.hap.Characteristic.StatusActive, this.status.availability);
|
|
374
|
+
occupancyService.getCharacteristic(this.hap.Characteristic.StatusActive).onGet(() => {
|
|
375
|
+
return this.status.availability;
|
|
376
|
+
});
|
|
377
|
+
this.log.info("Enabling occupancy sensor. Occupancy event duration set to %s seconds.", this.hints.motionOccupancyDuration);
|
|
378
|
+
return true;
|
|
379
|
+
}
|
|
167
380
|
// Open or close the garage door.
|
|
168
381
|
setDoorState(value) {
|
|
169
|
-
const actionExisting = this.status.door === this.hap.Characteristic.CurrentDoorState.OPENING ? "opening" : "closing";
|
|
170
382
|
const actionAttempt = value === this.hap.Characteristic.TargetDoorState.CLOSED ? "close" : "open";
|
|
171
383
|
// If this garage door is read-only, we won't process any requests to set state.
|
|
172
384
|
if (this.hints.readOnly) {
|
|
173
385
|
this.log.info("Unable to %s door. The door has been configured to be read only.", actionAttempt);
|
|
174
386
|
// Tell HomeKit that we haven't in fact changed our state so we don't end up in an inadvertent opening or closing state.
|
|
175
387
|
setImmediate(() => {
|
|
176
|
-
|
|
177
|
-
(_a = this.accessory.getService(this.hap.Service.GarageDoorOpener)) === null || _a === void 0 ? void 0 : _a.updateCharacteristic(this.hap.Characteristic.TargetDoorState, value === this.hap.Characteristic.TargetDoorState.CLOSED ? this.hap.Characteristic.TargetDoorState.OPEN : this.hap.Characteristic.TargetDoorState.CLOSED);
|
|
388
|
+
this.accessory.getService(this.hap.Service.GarageDoorOpener)?.updateCharacteristic(this.hap.Characteristic.TargetDoorState, value === this.hap.Characteristic.TargetDoorState.CLOSED ? this.hap.Characteristic.TargetDoorState.OPEN : this.hap.Characteristic.TargetDoorState.CLOSED);
|
|
178
389
|
});
|
|
179
390
|
return false;
|
|
180
391
|
}
|
|
181
|
-
// If we are already opening or closing the garage door, we
|
|
182
|
-
// change in the future, but for now, we manage this edge case by eliminating the possibility of doing so.
|
|
392
|
+
// If we are already opening or closing the garage door, we assume the user wants to stop the garage door opener at it's current location.
|
|
183
393
|
if ((this.status.door === this.hap.Characteristic.CurrentDoorState.OPENING) || (this.status.door === this.hap.Characteristic.CurrentDoorState.CLOSING)) {
|
|
184
|
-
this.log.
|
|
185
|
-
|
|
394
|
+
this.log.debug("Stop requested from user while transitioning between open and close states.");
|
|
395
|
+
// Execute the stop command.
|
|
396
|
+
this.command("door", "stop");
|
|
397
|
+
return true;
|
|
186
398
|
}
|
|
187
399
|
// Close the garage door.
|
|
188
400
|
if (value === this.hap.Characteristic.TargetDoorState.CLOSED) {
|
|
189
401
|
// HomeKit is asking us to close the garage door, but let's make sure it's not already closed first.
|
|
190
402
|
if (this.status.door !== this.hap.Characteristic.CurrentDoorState.CLOSED) {
|
|
191
403
|
// Execute the command.
|
|
192
|
-
this.command("door", "close"
|
|
404
|
+
this.command("door", "close");
|
|
193
405
|
}
|
|
194
406
|
return true;
|
|
195
407
|
}
|
|
@@ -198,7 +410,7 @@ class ratgdoAccessory {
|
|
|
198
410
|
// HomeKit is informing us to open the door, but we don't want to act if it's already open.
|
|
199
411
|
if (this.status.door !== this.hap.Characteristic.CurrentDoorState.OPEN) {
|
|
200
412
|
// Execute the command.
|
|
201
|
-
this.command("door", "open"
|
|
413
|
+
this.command("door", "open");
|
|
202
414
|
}
|
|
203
415
|
return true;
|
|
204
416
|
}
|
|
@@ -208,18 +420,31 @@ class ratgdoAccessory {
|
|
|
208
420
|
}
|
|
209
421
|
// Update the state of the accessory.
|
|
210
422
|
updateState(event, payload) {
|
|
211
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
212
423
|
const camelCase = (text) => text.charAt(0).toUpperCase() + text.slice(1);
|
|
424
|
+
const doorOccupancyService = this.accessory.getServiceById(this.hap.Service.OccupancySensor, RatgdoReservedNames.OCCUPANCY_SENSOR_DOOR_OPEN);
|
|
425
|
+
const garageDoorService = this.accessory.getService(this.hap.Service.GarageDoorOpener);
|
|
426
|
+
const lightBulbService = this.accessory.getService(this.hap.Service.Lightbulb);
|
|
427
|
+
const motionOccupancyService = this.accessory.getServiceById(this.hap.Service.OccupancySensor, RatgdoReservedNames.OCCUPANCY_SENSOR_MOTION);
|
|
428
|
+
const motionService = this.accessory.getService(this.hap.Service.MotionSensor);
|
|
429
|
+
const switchService = this.accessory.getServiceById(this.hap.Service.Switch, RatgdoReservedNames.SWITCH_OPENER_AUTOMATION);
|
|
430
|
+
// We continuously rebroadcast our device information.
|
|
431
|
+
this.configureInfo();
|
|
213
432
|
switch (event) {
|
|
214
433
|
case "availability":
|
|
215
434
|
this.status.availability = payload === "online";
|
|
216
435
|
// Update our availability.
|
|
217
|
-
|
|
218
|
-
|
|
436
|
+
garageDoorService?.updateCharacteristic(this.hap.Characteristic.StatusActive, this.status.availability);
|
|
437
|
+
doorOccupancyService?.updateCharacteristic(this.hap.Characteristic.StatusActive, this.status.availability);
|
|
438
|
+
motionOccupancyService?.updateCharacteristic(this.hap.Characteristic.StatusActive, this.status.availability);
|
|
439
|
+
motionService?.updateCharacteristic(this.hap.Characteristic.StatusActive, this.status.availability);
|
|
219
440
|
// Inform the user:
|
|
220
441
|
this.log.info("Device %s.", this.status.availability ? "connected" : "disconnected");
|
|
221
442
|
break;
|
|
222
443
|
case "door":
|
|
444
|
+
// If we're already in the state we're updating to, we're done.
|
|
445
|
+
if (this.translateCurrentDoorState(this.status.door) === payload) {
|
|
446
|
+
break;
|
|
447
|
+
}
|
|
223
448
|
// Clear out our door transition timer, if we have one.
|
|
224
449
|
if (this.doorTimer) {
|
|
225
450
|
clearTimeout(this.doorTimer);
|
|
@@ -231,28 +456,37 @@ class ratgdoAccessory {
|
|
|
231
456
|
break;
|
|
232
457
|
case "closing":
|
|
233
458
|
this.status.door = this.hap.Characteristic.CurrentDoorState.CLOSING;
|
|
234
|
-
// As a safety measure for occasionally unreliable MQTT message delivery, let's ensure we generate a closed state after a reasonable transition period. If we
|
|
235
|
-
// an actual state update before this, this safety measure won't be triggered.
|
|
459
|
+
// As a safety measure for occasionally unreliable MQTT message delivery, let's ensure we generate a closed state after a reasonable transition period. If we
|
|
460
|
+
// receive an actual state update before this, this safety measure won't be triggered.
|
|
236
461
|
this.doorTimer = setTimeout(() => {
|
|
237
462
|
// Mark the door as closed.
|
|
238
463
|
this.log.debug("Generating a close event to complete the state transition.");
|
|
239
464
|
this.updateState("door", "closed");
|
|
240
465
|
this.doorTimer = null;
|
|
241
|
-
},
|
|
466
|
+
}, RATGDO_TRANSITION_DURATION * 1000);
|
|
242
467
|
break;
|
|
243
468
|
case "open":
|
|
244
469
|
this.status.door = this.hap.Characteristic.CurrentDoorState.OPEN;
|
|
470
|
+
// Trigger our occupancy timer, if configured to do so and we don't have one yet.
|
|
471
|
+
if (this.hints.doorOpenOccupancySensor && !this.doorOccupancyTimer) {
|
|
472
|
+
this.doorOccupancyTimer = setTimeout(() => {
|
|
473
|
+
doorOccupancyService?.updateCharacteristic(this.hap.Characteristic.OccupancyDetected, true);
|
|
474
|
+
this.log.info("Garage door open occupancy detected.");
|
|
475
|
+
// Publish to MQTT, if the user has configured it.
|
|
476
|
+
this.platform.mqtt?.publish(this, "dooropenoccupancy", "true");
|
|
477
|
+
}, this.hints.doorOpenOccupancyDuration * 1000);
|
|
478
|
+
}
|
|
245
479
|
break;
|
|
246
480
|
case "opening":
|
|
247
481
|
this.status.door = this.hap.Characteristic.CurrentDoorState.OPENING;
|
|
248
|
-
// As a safety measure for occasionally unreliable MQTT message delivery, let's ensure we generate an open state after a reasonable transition period. If we
|
|
249
|
-
// an actual state update before this, this safety measure won't be triggered.
|
|
482
|
+
// As a safety measure for occasionally unreliable MQTT message delivery, let's ensure we generate an open state after a reasonable transition period. If we
|
|
483
|
+
// receive an actual state update before this, this safety measure won't be triggered.
|
|
250
484
|
this.doorTimer = setTimeout(() => {
|
|
251
485
|
// Mark the door as open.
|
|
252
486
|
this.log.debug("Generating an open event to complete the state transition.");
|
|
253
487
|
this.updateState("door", "open");
|
|
254
488
|
this.doorTimer = null;
|
|
255
|
-
},
|
|
489
|
+
}, RATGDO_TRANSITION_DURATION * 1000);
|
|
256
490
|
break;
|
|
257
491
|
case "stopped":
|
|
258
492
|
this.status.door = this.hap.Characteristic.CurrentDoorState.STOPPED;
|
|
@@ -264,26 +498,53 @@ class ratgdoAccessory {
|
|
|
264
498
|
// We are only going to update the target state if our current state is NOT stopped. If we are stopped, we are at the target state by definition. We also want to
|
|
265
499
|
// ensure we update TargetDoorState before updating CurrentDoorState in order to work around some notification quirks HomeKit occasionally has.
|
|
266
500
|
if (this.status.door !== this.hap.Characteristic.CurrentDoorState.STOPPED) {
|
|
267
|
-
|
|
501
|
+
garageDoorService?.updateCharacteristic(this.hap.Characteristic.TargetDoorState, this.doorTargetStateBias(this.status.door));
|
|
268
502
|
}
|
|
269
|
-
|
|
503
|
+
garageDoorService?.updateCharacteristic(this.hap.Characteristic.CurrentDoorState, this.status.door);
|
|
504
|
+
// Update our automation switch, if configured.
|
|
505
|
+
switchService?.updateCharacteristic(this.hap.Characteristic.On, this.doorTargetStateBias(this.status.door) === this.hap.Characteristic.TargetDoorState.OPEN);
|
|
270
506
|
// Inform the user:
|
|
271
507
|
this.log.info("%s.", camelCase(payload));
|
|
508
|
+
// If we have an open occupancy sensor configured and our door state is anything other than open, clear our occupancy state.
|
|
509
|
+
if (this.hints.doorOpenOccupancySensor && this.doorOccupancyTimer && (payload !== "open")) {
|
|
510
|
+
clearTimeout(this.doorOccupancyTimer);
|
|
511
|
+
this.doorOccupancyTimer = null;
|
|
512
|
+
if (doorOccupancyService?.getCharacteristic(this.hap.Characteristic.OccupancyDetected).value) {
|
|
513
|
+
doorOccupancyService?.updateCharacteristic(this.hap.Characteristic.OccupancyDetected, false);
|
|
514
|
+
this.log.info("Garage door open occupancy no longer detected.");
|
|
515
|
+
// Publish to MQTT, if the user has configured it.
|
|
516
|
+
this.platform.mqtt?.publish(this, "dooropenoccupancy", "false");
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
// Publish to MQTT, if the user has configured it.
|
|
520
|
+
this.platform.mqtt?.publish(this, "garagedoor", payload);
|
|
272
521
|
break;
|
|
273
522
|
case "light":
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
523
|
+
// Only act if we're not already at the state we're updating to.
|
|
524
|
+
if (this.status.light !== (payload === "on")) {
|
|
525
|
+
this.status.light = payload === "on";
|
|
526
|
+
lightBulbService?.updateCharacteristic(this.hap.Characteristic.On, this.status.light);
|
|
527
|
+
// Inform the user:
|
|
528
|
+
this.log.info("Light %s.", payload);
|
|
529
|
+
// Publish to MQTT, if the user has configured it.
|
|
530
|
+
this.platform.mqtt?.publish(this, "light", this.status.light.toString());
|
|
531
|
+
}
|
|
278
532
|
break;
|
|
279
533
|
case "lock":
|
|
280
|
-
//
|
|
534
|
+
// If we're already in the state we're updating to, we're done.
|
|
535
|
+
if (this.status.lock === (payload === "locked" ? this.hap.Characteristic.LockCurrentState.SECURED : this.hap.Characteristic.LockCurrentState.UNSECURED)) {
|
|
536
|
+
break;
|
|
537
|
+
}
|
|
538
|
+
// Determine our lock state.
|
|
281
539
|
this.status.lock = payload === "locked" ? this.hap.Characteristic.LockCurrentState.SECURED : this.hap.Characteristic.LockCurrentState.UNSECURED;
|
|
282
540
|
// Update our lock state.
|
|
283
|
-
|
|
284
|
-
|
|
541
|
+
garageDoorService?.updateCharacteristic(this.hap.Characteristic.LockTargetState, payload === "locked" ?
|
|
542
|
+
this.hap.Characteristic.LockTargetState.SECURED : this.hap.Characteristic.LockTargetState.UNSECURED);
|
|
543
|
+
garageDoorService?.updateCharacteristic(this.hap.Characteristic.LockCurrentState, this.status.lock);
|
|
285
544
|
// Inform the user:
|
|
286
545
|
this.log.info("%s.", camelCase(payload));
|
|
546
|
+
// Publish to MQTT, if the user has configured it.
|
|
547
|
+
this.platform.mqtt?.publish(this, "lock", this.status.lock.toString());
|
|
287
548
|
break;
|
|
288
549
|
case "motion":
|
|
289
550
|
this.status.motion = payload === "detected";
|
|
@@ -294,30 +555,92 @@ class ratgdoAccessory {
|
|
|
294
555
|
break;
|
|
295
556
|
}
|
|
296
557
|
// Update the motion sensor state.
|
|
297
|
-
|
|
298
|
-
// 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
|
|
299
|
-
// let the user know.
|
|
300
|
-
|
|
558
|
+
motionService?.updateCharacteristic(this.hap.Characteristic.MotionDetected, this.status.motion);
|
|
559
|
+
// 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
|
|
560
|
+
// cycle, let the user know.
|
|
561
|
+
if (this.motionTimer) {
|
|
562
|
+
clearTimeout(this.motionTimer);
|
|
563
|
+
}
|
|
564
|
+
else {
|
|
565
|
+
this.log.info("Motion detected.");
|
|
566
|
+
// Publish to MQTT, if the user has configured it.
|
|
567
|
+
this.platform.mqtt?.publish(this, "motion", this.status.motion.toString());
|
|
568
|
+
}
|
|
301
569
|
// Set a timer for the motion event.
|
|
302
570
|
this.motionTimer = setTimeout(() => {
|
|
303
|
-
var _a;
|
|
304
571
|
this.status.motion = false;
|
|
305
|
-
|
|
306
|
-
|
|
572
|
+
motionService?.updateCharacteristic(this.hap.Characteristic.MotionDetected, this.status.motion);
|
|
573
|
+
// Publish to MQTT, if the user has configured it.
|
|
574
|
+
this.platform.mqtt?.publish(this, "motion", this.status.motion.toString());
|
|
575
|
+
}, RATGDO_MOTION_DURATION * 1000);
|
|
576
|
+
// Kill any inflight occupancy sensor.
|
|
577
|
+
if (this.motionOccupancyTimer) {
|
|
578
|
+
clearTimeout(this.motionOccupancyTimer);
|
|
579
|
+
this.motionOccupancyTimer = null;
|
|
580
|
+
}
|
|
581
|
+
// If the motion occupancy sensor isn't already triggered, let's do so now.
|
|
582
|
+
if (motionOccupancyService?.getCharacteristic(this.hap.Characteristic.OccupancyDetected).value !== true) {
|
|
583
|
+
// Trigger the occupancy event in HomeKit.
|
|
584
|
+
motionOccupancyService?.updateCharacteristic(this.hap.Characteristic.OccupancyDetected, true);
|
|
585
|
+
// Publish to MQTT, if the user has configured it.
|
|
586
|
+
this.platform.mqtt?.publish(this, "occupancy", "true");
|
|
587
|
+
// Log the event.
|
|
588
|
+
this.log.info("Occupancy detected.");
|
|
589
|
+
}
|
|
590
|
+
// Reset our occupancy state after occupancyDuration.
|
|
591
|
+
this.motionOccupancyTimer = setTimeout(() => {
|
|
592
|
+
// Reset the occupancy sensor.
|
|
593
|
+
motionOccupancyService?.updateCharacteristic(this.hap.Characteristic.OccupancyDetected, false);
|
|
594
|
+
// Publish to MQTT, if the user has configured it.
|
|
595
|
+
this.platform.mqtt?.publish(this, "occupancy", "false");
|
|
596
|
+
// Log the event.
|
|
597
|
+
this.log.info("Occupancy no longer detected.");
|
|
598
|
+
// Delete the timer.
|
|
599
|
+
this.motionOccupancyTimer = null;
|
|
600
|
+
}, this.hints.motionOccupancyDuration * 1000);
|
|
307
601
|
break;
|
|
308
602
|
case "obstruction":
|
|
309
|
-
this.
|
|
310
|
-
|
|
311
|
-
|
|
603
|
+
garageDoorService?.updateCharacteristic(this.hap.Characteristic.ObstructionDetected, payload === "obstructed");
|
|
604
|
+
// Only act if we're not already at the state we're updating to.
|
|
605
|
+
if (this.status.obstruction !== (payload === "obstructed")) {
|
|
606
|
+
this.status.obstruction = payload === "obstructed";
|
|
607
|
+
this.log.info("Obstruction %sdetected.", this.status.obstruction ? "" : "no longer ");
|
|
608
|
+
// Publish to MQTT, if the user has configured it.
|
|
609
|
+
this.platform.mqtt?.publish(this, "obstruction", this.status.obstruction.toString());
|
|
610
|
+
}
|
|
312
611
|
break;
|
|
313
612
|
case "default":
|
|
314
613
|
break;
|
|
315
614
|
}
|
|
316
615
|
}
|
|
616
|
+
// Utility function to translate HomeKit's current door state values into human-readable form.
|
|
617
|
+
translateCurrentDoorState(value) {
|
|
618
|
+
// HomeKit state decoder ring.
|
|
619
|
+
switch (value) {
|
|
620
|
+
case this.hap.Characteristic.CurrentDoorState.CLOSED:
|
|
621
|
+
return "closed";
|
|
622
|
+
break;
|
|
623
|
+
case this.hap.Characteristic.CurrentDoorState.CLOSING:
|
|
624
|
+
return "closing";
|
|
625
|
+
break;
|
|
626
|
+
case this.hap.Characteristic.CurrentDoorState.OPEN:
|
|
627
|
+
return "open";
|
|
628
|
+
break;
|
|
629
|
+
case this.hap.Characteristic.CurrentDoorState.OPENING:
|
|
630
|
+
return "opening";
|
|
631
|
+
break;
|
|
632
|
+
case this.hap.Characteristic.CurrentDoorState.STOPPED:
|
|
633
|
+
return "stopped";
|
|
634
|
+
break;
|
|
635
|
+
default:
|
|
636
|
+
break;
|
|
637
|
+
}
|
|
638
|
+
return "unknown";
|
|
639
|
+
}
|
|
317
640
|
// Utility function to return our bias for what the current door state should be. This is primarily used for our initial bias on startup.
|
|
318
641
|
doorCurrentStateBias(state) {
|
|
319
|
-
// Our current door state reflects our opinion on what open or closed means in HomeKit terms. For the obvious states, this is easy. For some of the edge cases, it can
|
|
320
|
-
// less so. Our north star is that if we are in an obstructed state, we are open.
|
|
642
|
+
// Our current door state reflects our opinion on what open or closed means in HomeKit terms. For the obvious states, this is easy. For some of the edge cases, it can
|
|
643
|
+
// be less so. Our north star is that if we are in an obstructed state, we are open.
|
|
321
644
|
if (this.status.obstruction) {
|
|
322
645
|
return this.hap.Characteristic.CurrentDoorState.OPEN;
|
|
323
646
|
}
|
|
@@ -338,9 +661,9 @@ class ratgdoAccessory {
|
|
|
338
661
|
}
|
|
339
662
|
// Utility function to return our bias for what the target door state should be.
|
|
340
663
|
doorTargetStateBias(state) {
|
|
341
|
-
// We need to be careful with respect to the target state and we need to make some reasonable assumptions about where we intend to end up. If we are opening or
|
|
342
|
-
// target state needs to be the completion of those actions. If we're stopped or obstructed, we're going to assume the desired target state is to be
|
|
343
|
-
// typical opener behavior, and it's impossible for us to know with reasonable certainty what the original intention of the action was.
|
|
664
|
+
// We need to be careful with respect to the target state and we need to make some reasonable assumptions about where we intend to end up. If we are opening or
|
|
665
|
+
// closing, our target state needs to be the completion of those actions. If we're stopped or obstructed, we're going to assume the desired target state is to be
|
|
666
|
+
// open, since that is the typical opener behavior, and it's impossible for us to know with reasonable certainty what the original intention of the action was.
|
|
344
667
|
if (this.status.obstruction) {
|
|
345
668
|
return this.hap.Characteristic.TargetDoorState.OPEN;
|
|
346
669
|
}
|
|
@@ -372,31 +695,52 @@ class ratgdoAccessory {
|
|
|
372
695
|
}
|
|
373
696
|
}
|
|
374
697
|
// Utility function to transmit a command to Ratgdo.
|
|
375
|
-
command(topic, payload
|
|
376
|
-
this.platform.broker.publish({ cmd: "publish", dup: false, payload: payload, qos: 2, retain: false, topic: this.device.name + "/command/" + topic }, () => {
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
698
|
+
command(topic, payload) {
|
|
699
|
+
this.platform.broker.publish({ cmd: "publish", dup: false, payload: payload, qos: 2, retain: false, topic: this.device.name + "/command/" + topic }, (error) => {
|
|
700
|
+
if (error) {
|
|
701
|
+
this.log.error("Publish error:");
|
|
702
|
+
this.log.error(util.inspect(error), { colors: true, depth: null, sorted: true });
|
|
703
|
+
}
|
|
704
|
+
});
|
|
381
705
|
}
|
|
382
706
|
// Utility function to return a floating point configuration parameter on a device.
|
|
383
707
|
getFeatureFloat(option) {
|
|
384
|
-
return
|
|
708
|
+
return getOptionFloat(getOptionValue(this.platform.configOptions, this.device, option));
|
|
385
709
|
}
|
|
386
710
|
// Utility function to return an integer configuration parameter on a device.
|
|
387
711
|
getFeatureNumber(option) {
|
|
388
|
-
return
|
|
712
|
+
return getOptionNumber(getOptionValue(this.platform.configOptions, this.device, option));
|
|
389
713
|
}
|
|
390
714
|
// Utility for checking feature options on a device.
|
|
391
715
|
hasFeature(option) {
|
|
392
|
-
return
|
|
716
|
+
return isOptionEnabled(this.platform.configOptions, this.device, option, this.platform.featureOptionDefault(option));
|
|
717
|
+
}
|
|
718
|
+
// Utility function to set the name of a service.
|
|
719
|
+
setServiceName(service, name) {
|
|
720
|
+
if (!service) {
|
|
721
|
+
return;
|
|
722
|
+
}
|
|
723
|
+
service.displayName = name;
|
|
724
|
+
service.updateCharacteristic(this.hap.Characteristic.ConfiguredName, name);
|
|
393
725
|
}
|
|
394
|
-
//
|
|
726
|
+
// Utility function to return the name of this device.
|
|
395
727
|
get name() {
|
|
396
|
-
|
|
397
|
-
const configuredName =
|
|
398
|
-
|
|
728
|
+
// We use the garage door service as the natural proxy for the name.
|
|
729
|
+
const configuredName = this.accessory.getService(this.hap.Service.GarageDoorOpener)?.getCharacteristic(this.hap.Characteristic.ConfiguredName).value;
|
|
730
|
+
// If we don't have a name for the garage door service, return the device name from Ratgdo.
|
|
731
|
+
return configuredName?.length ? configuredName : this.device.name;
|
|
732
|
+
}
|
|
733
|
+
// Utility function to return the current accessory name of this device.
|
|
734
|
+
get accessoryName() {
|
|
735
|
+
return this.accessory.getService(this.hap.Service.AccessoryInformation)?.getCharacteristic(this.hap.Characteristic.Name).value ?? this.device.name;
|
|
736
|
+
}
|
|
737
|
+
// Utility function to set the current accessory name of this device.
|
|
738
|
+
set accessoryName(name) {
|
|
739
|
+
// Set all the internally managed names within Homebridge to the new accessory name.
|
|
740
|
+
this.accessory.displayName = name;
|
|
741
|
+
this.accessory._associatedHAPAccessory.displayName = name;
|
|
742
|
+
// Set all the HomeKit-visible names.
|
|
743
|
+
this.accessory.getService(this.hap.Service.AccessoryInformation)?.updateCharacteristic(this.hap.Characteristic.Name, name);
|
|
399
744
|
}
|
|
400
745
|
}
|
|
401
|
-
exports.ratgdoAccessory = ratgdoAccessory;
|
|
402
746
|
//# sourceMappingURL=ratgdo-device.js.map
|