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