homebridge-ratgdo 0.1.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 +13 -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 +464 -96
- 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 +65 -32
- 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 -9
- 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,28 +1,38 @@
|
|
|
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;
|
|
14
23
|
this.api = platform.api;
|
|
15
24
|
this.status = {};
|
|
16
25
|
this.config = platform.config;
|
|
26
|
+
this.doorTimer = null;
|
|
17
27
|
this.hap = this.api.hap;
|
|
18
28
|
this.hints = {};
|
|
19
29
|
this.device = device;
|
|
20
30
|
this.platform = platform;
|
|
21
31
|
this.log = {
|
|
22
|
-
debug: (message, ...parameters) => platform.debug(
|
|
23
|
-
error: (message, ...parameters) => platform.log.error(
|
|
24
|
-
info: (message, ...parameters) => platform.log.info(
|
|
25
|
-
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))
|
|
26
36
|
};
|
|
27
37
|
// Initialize our internal state.
|
|
28
38
|
this.status.availability = false;
|
|
@@ -31,9 +41,10 @@ class ratgdoAccessory {
|
|
|
31
41
|
this.status.lock = this.hap.Characteristic.LockCurrentState.UNSECURED;
|
|
32
42
|
this.status.motion = false;
|
|
33
43
|
this.status.obstruction = false;
|
|
44
|
+
this.doorOccupancyTimer = null;
|
|
45
|
+
this.motionOccupancyTimer = null;
|
|
34
46
|
this.motionTimer = null;
|
|
35
47
|
this.obstructionTimer = null;
|
|
36
|
-
this.occupancyTimer = null;
|
|
37
48
|
this.configureDevice();
|
|
38
49
|
}
|
|
39
50
|
// Configure a garage door accessory for HomeKit.
|
|
@@ -44,40 +55,102 @@ class ratgdoAccessory {
|
|
|
44
55
|
this.configureHints();
|
|
45
56
|
this.configureInfo();
|
|
46
57
|
this.configureGarageDoor();
|
|
58
|
+
this.configureMqtt();
|
|
59
|
+
this.configureAutomationSwitch();
|
|
60
|
+
this.configureDoorOpenOccupancySensor();
|
|
47
61
|
this.configureLight();
|
|
48
62
|
this.configureMotionSensor();
|
|
49
|
-
|
|
50
|
-
// this.configureOccupancySensor();
|
|
63
|
+
this.configureMotionOccupancySensor();
|
|
51
64
|
}
|
|
52
65
|
// Configure device-specific settings.
|
|
53
66
|
configureHints() {
|
|
54
67
|
this.hints.automationSwitch = this.hasFeature("Opener.Switch");
|
|
55
|
-
this.hints.
|
|
56
|
-
|
|
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");
|
|
57
74
|
this.hints.readOnly = this.hasFeature("Opener.ReadOnly");
|
|
58
|
-
this.hints.
|
|
75
|
+
this.hints.syncName = this.hasFeature("Device.SyncName");
|
|
59
76
|
return true;
|
|
60
77
|
}
|
|
61
78
|
// Configure the device information for HomeKit.
|
|
62
79
|
configureInfo() {
|
|
63
|
-
var _a, _b, _c, _d;
|
|
64
80
|
// Update the manufacturer information for this device.
|
|
65
|
-
|
|
81
|
+
this.accessory.getService(this.hap.Service.AccessoryInformation)?.updateCharacteristic(this.hap.Characteristic.Manufacturer, "github.com/hjdhjd");
|
|
66
82
|
// Update the model information for this device.
|
|
67
|
-
|
|
83
|
+
this.accessory.getService(this.hap.Service.AccessoryInformation)?.updateCharacteristic(this.hap.Characteristic.Model, "Ratgdo");
|
|
68
84
|
// Update the serial number for this device.
|
|
69
|
-
|
|
85
|
+
this.accessory.getService(this.hap.Service.AccessoryInformation)?.updateCharacteristic(this.hap.Characteristic.SerialNumber, this.device.mac);
|
|
70
86
|
// Update the firmware information for this device.
|
|
71
|
-
|
|
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
|
+
}
|
|
72
145
|
return true;
|
|
73
146
|
}
|
|
74
147
|
// Configure the garage door service for HomeKit.
|
|
75
148
|
configureGarageDoor() {
|
|
76
|
-
var _a;
|
|
77
149
|
let garageDoorService = this.accessory.getService(this.hap.Service.GarageDoorOpener);
|
|
78
150
|
// Add the garage door opener service to the accessory, if needed.
|
|
79
151
|
if (!garageDoorService) {
|
|
80
152
|
garageDoorService = new this.hap.Service.GarageDoorOpener(this.name);
|
|
153
|
+
garageDoorService.addOptionalCharacteristic(this.hap.Characteristic.ConfiguredName);
|
|
81
154
|
this.accessory.addService(garageDoorService);
|
|
82
155
|
}
|
|
83
156
|
// Set the initial current and target door states to closed since ratgdo doesn't tell us initial state over MQTT on startup.
|
|
@@ -91,23 +164,30 @@ class ratgdoAccessory {
|
|
|
91
164
|
garageDoorService.getCharacteristic(this.hap.Characteristic.CurrentDoorState).onGet(() => this.status.door);
|
|
92
165
|
// Inform HomeKit of any obstructions.
|
|
93
166
|
garageDoorService.getCharacteristic(this.hap.Characteristic.ObstructionDetected).onGet(() => this.status.obstruction === true);
|
|
94
|
-
//
|
|
95
|
-
garageDoorService.addOptionalCharacteristic(this.hap.Characteristic.LockCurrentState);
|
|
96
|
-
garageDoorService.addOptionalCharacteristic(this.hap.Characteristic.LockTargetState);
|
|
167
|
+
// Configure the lock garage door lock current and target state characteristics.
|
|
97
168
|
garageDoorService.updateCharacteristic(this.hap.Characteristic.LockCurrentState, this.status.lock);
|
|
98
169
|
garageDoorService.updateCharacteristic(this.hap.Characteristic.LockTargetState, this.lockTargetStateBias(this.status.lock));
|
|
99
|
-
// Add the configured name for this device.
|
|
100
|
-
garageDoorService.addOptionalCharacteristic(this.hap.Characteristic.ConfiguredName);
|
|
101
|
-
// switchService.updateCharacteristic(this.hap.Characteristic.ConfiguredName, switchName);
|
|
102
170
|
// Update our configured name, if requested.
|
|
103
|
-
if (this.hints.
|
|
104
|
-
|
|
105
|
-
if (this.hints.
|
|
106
|
-
(
|
|
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");
|
|
107
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;
|
|
108
190
|
}
|
|
109
|
-
// Add our status active characteristic.
|
|
110
|
-
garageDoorService.addOptionalCharacteristic(this.hap.Characteristic.StatusActive);
|
|
111
191
|
garageDoorService.getCharacteristic(this.hap.Characteristic.StatusActive).onGet(() => this.status.availability);
|
|
112
192
|
garageDoorService.updateCharacteristic(this.hap.Characteristic.StatusActive, this.status.availability);
|
|
113
193
|
// Let HomeKit know that this is the primary service on this accessory.
|
|
@@ -116,9 +196,16 @@ class ratgdoAccessory {
|
|
|
116
196
|
}
|
|
117
197
|
// Configure the light for HomeKit.
|
|
118
198
|
configureLight() {
|
|
119
|
-
var _a, _b;
|
|
120
199
|
// Find the service, if it exists.
|
|
121
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
|
+
}
|
|
122
209
|
// Add the service to the accessory, if needed.
|
|
123
210
|
if (!lightService) {
|
|
124
211
|
lightService = new this.hap.Service.Lightbulb(this.name);
|
|
@@ -126,24 +213,34 @@ class ratgdoAccessory {
|
|
|
126
213
|
this.log.error("Unable to add the light.");
|
|
127
214
|
return false;
|
|
128
215
|
}
|
|
216
|
+
lightService.addOptionalCharacteristic(this.hap.Characteristic.ConfiguredName);
|
|
129
217
|
this.accessory.addService(lightService);
|
|
130
218
|
this.log.info("Enabling light.");
|
|
131
219
|
}
|
|
132
|
-
// Turn the light on or off.
|
|
133
|
-
(_a = lightService.getCharacteristic(this.hap.Characteristic.On)) === null || _a === void 0 ? void 0 : _a.onGet(() => this.status.light);
|
|
134
|
-
(_b = lightService.getCharacteristic(this.hap.Characteristic.On)) === null || _b === void 0 ? void 0 : _b.onSet((value) => {
|
|
135
|
-
this.platform.broker.publish({ cmd: "publish", dup: false, payload: value === true ? "on" : "off", qos: 0, retain: false, topic: this.device.name + "/command/light" }, () => { });
|
|
136
|
-
});
|
|
137
220
|
// Initialize the light.
|
|
138
221
|
lightService.displayName = this.name;
|
|
139
222
|
lightService.updateCharacteristic(this.hap.Characteristic.Name, this.name);
|
|
223
|
+
lightService.updateCharacteristic(this.hap.Characteristic.ConfiguredName, this.name);
|
|
140
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
|
+
});
|
|
141
230
|
return true;
|
|
142
231
|
}
|
|
143
232
|
// Configure the motion sensor for HomeKit.
|
|
144
233
|
configureMotionSensor() {
|
|
145
234
|
// Find the motion sensor service, if it exists.
|
|
146
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
|
+
}
|
|
147
244
|
// We don't have a motion sensor, let's add it to the device.
|
|
148
245
|
if (!motionService) {
|
|
149
246
|
// We don't have it, add the motion sensor to the device.
|
|
@@ -152,17 +249,132 @@ class ratgdoAccessory {
|
|
|
152
249
|
this.log.error("Unable to add the motion sensor.");
|
|
153
250
|
return false;
|
|
154
251
|
}
|
|
252
|
+
motionService.addOptionalCharacteristic(this.hap.Characteristic.ConfiguredName);
|
|
155
253
|
this.accessory.addService(motionService);
|
|
156
254
|
this.log.info("Enabling motion sensor.");
|
|
157
255
|
}
|
|
158
256
|
// Initialize the state of the motion sensor.
|
|
159
257
|
motionService.displayName = this.name;
|
|
160
258
|
motionService.updateCharacteristic(this.hap.Characteristic.Name, this.name);
|
|
259
|
+
motionService.updateCharacteristic(this.hap.Characteristic.ConfiguredName, this.name);
|
|
161
260
|
motionService.updateCharacteristic(this.hap.Characteristic.MotionDetected, false);
|
|
162
261
|
motionService.updateCharacteristic(this.hap.Characteristic.StatusActive, this.status.availability);
|
|
163
262
|
motionService.getCharacteristic(this.hap.Characteristic.StatusActive).onGet(() => this.status.availability);
|
|
164
263
|
return true;
|
|
165
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
|
+
}
|
|
166
378
|
// Open or close the garage door.
|
|
167
379
|
setDoorState(value) {
|
|
168
380
|
const actionExisting = this.status.door === this.hap.Characteristic.CurrentDoorState.OPENING ? "opening" : "closing";
|
|
@@ -172,15 +384,14 @@ class ratgdoAccessory {
|
|
|
172
384
|
this.log.info("Unable to %s door. The door has been configured to be read only.", actionAttempt);
|
|
173
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.
|
|
174
386
|
setImmediate(() => {
|
|
175
|
-
|
|
176
|
-
(_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);
|
|
177
388
|
});
|
|
178
389
|
return false;
|
|
179
390
|
}
|
|
180
|
-
// If we are already opening or closing the garage door, we error out.
|
|
181
|
-
//
|
|
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.
|
|
182
393
|
if ((this.status.door === this.hap.Characteristic.CurrentDoorState.OPENING) || (this.status.door === this.hap.Characteristic.CurrentDoorState.CLOSING)) {
|
|
183
|
-
this.log.error("Unable to %s door while currently attempting to complete %s.
|
|
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);
|
|
184
395
|
return false;
|
|
185
396
|
}
|
|
186
397
|
// Close the garage door.
|
|
@@ -188,7 +399,7 @@ class ratgdoAccessory {
|
|
|
188
399
|
// HomeKit is asking us to close the garage door, but let's make sure it's not already closed first.
|
|
189
400
|
if (this.status.door !== this.hap.Characteristic.CurrentDoorState.CLOSED) {
|
|
190
401
|
// Execute the command.
|
|
191
|
-
this.
|
|
402
|
+
this.command("door", "close");
|
|
192
403
|
}
|
|
193
404
|
return true;
|
|
194
405
|
}
|
|
@@ -197,7 +408,7 @@ class ratgdoAccessory {
|
|
|
197
408
|
// HomeKit is informing us to open the door, but we don't want to act if it's already open.
|
|
198
409
|
if (this.status.door !== this.hap.Characteristic.CurrentDoorState.OPEN) {
|
|
199
410
|
// Execute the command.
|
|
200
|
-
this.
|
|
411
|
+
this.command("door", "open");
|
|
201
412
|
}
|
|
202
413
|
return true;
|
|
203
414
|
}
|
|
@@ -207,63 +418,129 @@ class ratgdoAccessory {
|
|
|
207
418
|
}
|
|
208
419
|
// Update the state of the accessory.
|
|
209
420
|
updateState(event, payload) {
|
|
210
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
211
|
-
let currentState, targetState;
|
|
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
|
+
}
|
|
444
|
+
// Clear out our door transition timer, if we have one.
|
|
445
|
+
if (this.doorTimer) {
|
|
446
|
+
clearTimeout(this.doorTimer);
|
|
447
|
+
this.doorTimer = null;
|
|
448
|
+
}
|
|
223
449
|
switch (payload) {
|
|
224
450
|
case "closed":
|
|
225
|
-
|
|
451
|
+
this.status.door = this.hap.Characteristic.CurrentDoorState.CLOSED;
|
|
226
452
|
break;
|
|
227
453
|
case "closing":
|
|
228
|
-
|
|
454
|
+
this.status.door = this.hap.Characteristic.CurrentDoorState.CLOSING;
|
|
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.
|
|
457
|
+
this.doorTimer = setTimeout(() => {
|
|
458
|
+
// Mark the door as closed.
|
|
459
|
+
this.log.debug("Generating a close event to complete the state transition.");
|
|
460
|
+
this.updateState("door", "closed");
|
|
461
|
+
this.doorTimer = null;
|
|
462
|
+
}, RATGDO_TRANSITION_DURATION * 1000);
|
|
229
463
|
break;
|
|
230
464
|
case "open":
|
|
231
|
-
|
|
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
|
+
}
|
|
232
475
|
break;
|
|
233
476
|
case "opening":
|
|
234
|
-
|
|
477
|
+
this.status.door = this.hap.Characteristic.CurrentDoorState.OPENING;
|
|
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.
|
|
480
|
+
this.doorTimer = setTimeout(() => {
|
|
481
|
+
// Mark the door as open.
|
|
482
|
+
this.log.debug("Generating an open event to complete the state transition.");
|
|
483
|
+
this.updateState("door", "open");
|
|
484
|
+
this.doorTimer = null;
|
|
485
|
+
}, RATGDO_TRANSITION_DURATION * 1000);
|
|
235
486
|
break;
|
|
236
487
|
case "stopped":
|
|
237
|
-
|
|
488
|
+
this.status.door = this.hap.Characteristic.CurrentDoorState.STOPPED;
|
|
238
489
|
break;
|
|
239
490
|
default:
|
|
240
|
-
|
|
491
|
+
this.status.door = this.hap.Characteristic.CurrentDoorState.CLOSED;
|
|
241
492
|
break;
|
|
242
493
|
}
|
|
243
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
|
|
244
495
|
// ensure we update TargetDoorState before updating CurrentDoorState in order to work around some notification quirks HomeKit occasionally has.
|
|
245
|
-
if (
|
|
246
|
-
|
|
496
|
+
if (this.status.door !== this.hap.Characteristic.CurrentDoorState.STOPPED) {
|
|
497
|
+
garageDoorService?.updateCharacteristic(this.hap.Characteristic.TargetDoorState, this.doorTargetStateBias(this.status.door));
|
|
247
498
|
}
|
|
248
|
-
|
|
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);
|
|
249
502
|
// Inform the user:
|
|
250
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);
|
|
251
517
|
break;
|
|
252
518
|
case "light":
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
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
|
+
}
|
|
257
528
|
break;
|
|
258
529
|
case "lock":
|
|
259
|
-
//
|
|
260
|
-
|
|
261
|
-
|
|
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.
|
|
535
|
+
this.status.lock = payload === "locked" ? this.hap.Characteristic.LockCurrentState.SECURED : this.hap.Characteristic.LockCurrentState.UNSECURED;
|
|
262
536
|
// Update our lock state.
|
|
263
|
-
(
|
|
264
|
-
|
|
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);
|
|
265
540
|
// Inform the user:
|
|
266
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());
|
|
267
544
|
break;
|
|
268
545
|
case "motion":
|
|
269
546
|
this.status.motion = payload === "detected";
|
|
@@ -274,30 +551,92 @@ class ratgdoAccessory {
|
|
|
274
551
|
break;
|
|
275
552
|
}
|
|
276
553
|
// Update the motion sensor state.
|
|
277
|
-
|
|
278
|
-
// 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
|
|
279
|
-
// let the user know.
|
|
280
|
-
|
|
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
|
+
}
|
|
281
565
|
// Set a timer for the motion event.
|
|
282
566
|
this.motionTimer = setTimeout(() => {
|
|
283
|
-
var _a;
|
|
284
567
|
this.status.motion = false;
|
|
285
|
-
|
|
286
|
-
|
|
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);
|
|
287
597
|
break;
|
|
288
598
|
case "obstruction":
|
|
289
|
-
this.
|
|
290
|
-
|
|
291
|
-
|
|
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
|
+
}
|
|
292
607
|
break;
|
|
293
608
|
case "default":
|
|
294
609
|
break;
|
|
295
610
|
}
|
|
296
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
|
+
}
|
|
297
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.
|
|
298
637
|
doorCurrentStateBias(state) {
|
|
299
|
-
// 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
|
|
300
|
-
// 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.
|
|
301
640
|
if (this.status.obstruction) {
|
|
302
641
|
return this.hap.Characteristic.CurrentDoorState.OPEN;
|
|
303
642
|
}
|
|
@@ -318,9 +657,9 @@ class ratgdoAccessory {
|
|
|
318
657
|
}
|
|
319
658
|
// Utility function to return our bias for what the target door state should be.
|
|
320
659
|
doorTargetStateBias(state) {
|
|
321
|
-
// 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
|
|
322
|
-
// 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
|
|
323
|
-
// 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.
|
|
324
663
|
if (this.status.obstruction) {
|
|
325
664
|
return this.hap.Characteristic.TargetDoorState.OPEN;
|
|
326
665
|
}
|
|
@@ -351,24 +690,53 @@ class ratgdoAccessory {
|
|
|
351
690
|
break;
|
|
352
691
|
}
|
|
353
692
|
}
|
|
693
|
+
// Utility function to transmit a command to Ratgdo.
|
|
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
|
+
});
|
|
701
|
+
}
|
|
354
702
|
// Utility function to return a floating point configuration parameter on a device.
|
|
355
703
|
getFeatureFloat(option) {
|
|
356
|
-
return
|
|
704
|
+
return getOptionFloat(getOptionValue(this.platform.configOptions, this.device, option));
|
|
357
705
|
}
|
|
358
706
|
// Utility function to return an integer configuration parameter on a device.
|
|
359
707
|
getFeatureNumber(option) {
|
|
360
|
-
return
|
|
708
|
+
return getOptionNumber(getOptionValue(this.platform.configOptions, this.device, option));
|
|
361
709
|
}
|
|
362
710
|
// Utility for checking feature options on a device.
|
|
363
711
|
hasFeature(option) {
|
|
364
|
-
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);
|
|
365
721
|
}
|
|
366
|
-
//
|
|
722
|
+
// Utility function to return the name of this device.
|
|
367
723
|
get name() {
|
|
368
|
-
|
|
369
|
-
const configuredName =
|
|
370
|
-
|
|
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);
|
|
371
740
|
}
|
|
372
741
|
}
|
|
373
|
-
exports.ratgdoAccessory = ratgdoAccessory;
|
|
374
742
|
//# sourceMappingURL=ratgdo-device.js.map
|