homebridge-unifi-protect 5.5.4 → 6.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/config.schema.json +17 -16
- package/dist/index.d.ts +3 -0
- package/dist/index.js +6 -6
- package/dist/index.js.map +1 -1
- package/dist/protect-camera.d.ts +58 -0
- package/dist/protect-camera.js +367 -246
- package/dist/protect-camera.js.map +1 -1
- package/dist/protect-device.d.ts +48 -0
- package/dist/protect-device.js +189 -0
- package/dist/protect-device.js.map +1 -0
- package/dist/protect-doorbell.d.ts +22 -0
- package/dist/protect-doorbell.js +75 -64
- package/dist/protect-doorbell.js.map +1 -1
- package/dist/protect-ffmpeg-record.d.ts +15 -0
- package/dist/protect-ffmpeg-record.js +48 -34
- package/dist/protect-ffmpeg-record.js.map +1 -1
- package/dist/protect-ffmpeg-stream.d.ts +15 -0
- package/dist/protect-ffmpeg-stream.js +22 -12
- package/dist/protect-ffmpeg-stream.js.map +1 -1
- package/dist/protect-ffmpeg.d.ts +42 -0
- package/dist/protect-ffmpeg.js +49 -58
- package/dist/protect-ffmpeg.js.map +1 -1
- package/dist/protect-light.d.ts +13 -0
- package/dist/protect-light.js +63 -40
- package/dist/protect-light.js.map +1 -1
- package/dist/protect-liveviews.d.ts +17 -0
- package/dist/protect-liveviews.js +117 -101
- package/dist/protect-liveviews.js.map +1 -1
- package/dist/protect-mqtt.d.ts +19 -0
- package/dist/protect-mqtt.js +26 -35
- package/dist/protect-mqtt.js.map +1 -1
- package/dist/protect-nvr-events.d.ts +30 -0
- package/dist/protect-nvr-events.js +168 -431
- package/dist/protect-nvr-events.js.map +1 -1
- package/dist/protect-nvr-systeminfo.d.ts +15 -0
- package/dist/protect-nvr-systeminfo.js +43 -49
- package/dist/protect-nvr-systeminfo.js.map +1 -1
- package/dist/protect-nvr.d.ts +48 -0
- package/dist/protect-nvr.js +327 -359
- package/dist/protect-nvr.js.map +1 -1
- package/dist/protect-options.d.ts +39 -0
- package/dist/protect-options.js +172 -6
- package/dist/protect-options.js.map +1 -1
- package/dist/protect-platform.d.ts +17 -0
- package/dist/protect-platform.js +17 -30
- package/dist/protect-platform.js.map +1 -1
- package/dist/protect-record.d.ts +33 -0
- package/dist/protect-record.js +130 -126
- package/dist/protect-record.js.map +1 -1
- package/dist/protect-rtp.d.ts +29 -0
- package/dist/protect-rtp.js +133 -16
- package/dist/protect-rtp.js.map +1 -1
- package/dist/protect-securitysystem.d.ts +18 -0
- package/dist/protect-securitysystem.js +105 -109
- package/dist/protect-securitysystem.js.map +1 -1
- package/dist/protect-sensor.d.ts +28 -0
- package/dist/protect-sensor.js +79 -97
- package/dist/protect-sensor.js.map +1 -1
- package/dist/protect-stream.d.ts +41 -0
- package/dist/protect-stream.js +298 -156
- package/dist/protect-stream.js.map +1 -1
- package/dist/protect-timeshift.d.ts +30 -0
- package/dist/protect-timeshift.js +65 -48
- package/dist/protect-timeshift.js.map +1 -1
- package/dist/protect-types.d.ts +50 -0
- package/dist/protect-types.js +22 -0
- package/dist/protect-types.js.map +1 -0
- package/dist/protect-viewer.d.ts +17 -0
- package/dist/protect-viewer.js +41 -47
- package/dist/protect-viewer.js.map +1 -1
- package/dist/settings.d.ts +22 -0
- package/dist/settings.js +30 -35
- package/dist/settings.js.map +1 -1
- package/homebridge-ui/public/index.html +715 -0
- package/homebridge-ui/server.js +156 -0
- package/package.json +15 -15
- package/dist/protect-accessory.js +0 -184
- package/dist/protect-accessory.js.map +0 -1
|
@@ -1,554 +1,291 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const protect_camera_1 = require("./protect-camera");
|
|
6
|
-
const protect_accessory_1 = require("./protect-accessory");
|
|
7
|
-
class ProtectNvrEvents {
|
|
1
|
+
import { ProtectReservedNames } from "./protect-types.js";
|
|
2
|
+
import { EventEmitter } from "node:events";
|
|
3
|
+
export class ProtectNvrEvents extends EventEmitter {
|
|
4
|
+
// Initialize an instance of our Protect events handler.
|
|
8
5
|
constructor(nvr) {
|
|
6
|
+
super();
|
|
9
7
|
this.api = nvr.platform.api;
|
|
10
|
-
this.debug = nvr.platform.debug.bind(nvr.platform);
|
|
11
8
|
this.eventTimers = {};
|
|
12
9
|
this.hap = nvr.platform.api.hap;
|
|
13
10
|
this.lastMotion = {};
|
|
14
11
|
this.lastRing = {};
|
|
15
|
-
this.log = nvr.
|
|
16
|
-
this.name = () => {
|
|
17
|
-
return nvr.nvrApi.getNvrName();
|
|
18
|
-
};
|
|
12
|
+
this.log = nvr.log;
|
|
19
13
|
this.nvr = nvr;
|
|
20
|
-
this.
|
|
14
|
+
this.ufpApi = nvr.ufpApi;
|
|
15
|
+
this.ufpDeviceState = {};
|
|
21
16
|
this.motionDuration = nvr.platform.config.motionDuration;
|
|
22
17
|
this.platform = nvr.platform;
|
|
23
18
|
this.ringDuration = nvr.platform.config.ringDuration;
|
|
24
19
|
this.unsupportedDevices = {};
|
|
25
|
-
this.
|
|
20
|
+
this.eventsHandler = null;
|
|
21
|
+
this.ufpUpdatesHandler = null;
|
|
22
|
+
this.configureEvents();
|
|
26
23
|
}
|
|
27
|
-
//
|
|
28
|
-
update
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
this.nvrApi.eventsWs.on("message", this.updatesListener = (event) => {
|
|
43
|
-
var _a, _b;
|
|
44
|
-
let nvrEvent;
|
|
45
|
-
try {
|
|
46
|
-
nvrEvent = JSON.parse(event.toString());
|
|
47
|
-
}
|
|
48
|
-
catch (error) {
|
|
49
|
-
if (error instanceof SyntaxError) {
|
|
50
|
-
this.log.error("%s: Unable to process message from the realtime system events API: \"%s\". Error: %s.", this.nvrApi.getNvrName(), event, error.message);
|
|
24
|
+
// Thanks to https://stackoverflow.com/a/48218209 for the foundation for this one.
|
|
25
|
+
// Merge Protect JSON update payloads into the Protect configuration JSON for a device while dealing with deep objects.
|
|
26
|
+
// @param {...object} objects - Objects to merge
|
|
27
|
+
// @returns {object} New object with merged key/values
|
|
28
|
+
patchUfpConfigJson(...objects) {
|
|
29
|
+
const isObject = (x) => (x && (typeof (x) === "object"));
|
|
30
|
+
return objects.reduce((prev, obj) => {
|
|
31
|
+
for (const key of Object.keys(obj)) {
|
|
32
|
+
const pVal = prev[key];
|
|
33
|
+
const oVal = obj[key];
|
|
34
|
+
if (Array.isArray(pVal) && Array.isArray(oVal)) {
|
|
35
|
+
prev[key] = oVal;
|
|
36
|
+
}
|
|
37
|
+
else if (isObject(pVal) && isObject(oVal)) {
|
|
38
|
+
prev[key] = this.patchUfpConfigJson(pVal, oVal);
|
|
51
39
|
}
|
|
52
40
|
else {
|
|
53
|
-
|
|
41
|
+
prev[key] = oVal;
|
|
54
42
|
}
|
|
55
|
-
// Errors mean that we're done now.
|
|
56
|
-
return;
|
|
57
|
-
}
|
|
58
|
-
// We're interested in device state change events.
|
|
59
|
-
if ((nvrEvent === null || nvrEvent === void 0 ? void 0 : nvrEvent.type) !== "DEVICE_STATE_CHANGED") {
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
62
|
-
// We only want Protect controllers.
|
|
63
|
-
const controller = (_b = (_a = nvrEvent.apps) === null || _a === void 0 ? void 0 : _a.controllers) === null || _b === void 0 ? void 0 : _b.find(x => x.name === "protect");
|
|
64
|
-
if (!controller) {
|
|
65
|
-
return;
|
|
66
43
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
44
|
+
return prev;
|
|
45
|
+
}, {});
|
|
46
|
+
}
|
|
47
|
+
// Process Protect API update events.
|
|
48
|
+
ufpUpdates(packet) {
|
|
49
|
+
let protectDevice;
|
|
50
|
+
switch (packet.header.modelKey) {
|
|
51
|
+
case "nvr":
|
|
52
|
+
this.nvr.ufp = this.patchUfpConfigJson(this.nvr.ufp, packet.payload);
|
|
53
|
+
break;
|
|
54
|
+
default:
|
|
55
|
+
// Lookup the device.
|
|
56
|
+
protectDevice = this.nvr.deviceLookup(packet.header.id);
|
|
57
|
+
// Update our device state.
|
|
58
|
+
if (protectDevice) {
|
|
59
|
+
protectDevice.ufp = this.patchUfpConfigJson(protectDevice.ufp, packet.payload);
|
|
60
|
+
}
|
|
61
|
+
break;
|
|
62
|
+
}
|
|
63
|
+
// Update the internal list we maintain.
|
|
64
|
+
this.ufpDeviceState[packet.header.id] = Object.assign(this.ufpDeviceState[packet.header.id] ?? {}, packet.payload);
|
|
65
|
+
}
|
|
66
|
+
// Process device additions and removals from the Protect update events API.
|
|
67
|
+
manageDevices(packet) {
|
|
68
|
+
const payload = packet.payload;
|
|
69
|
+
// We only want adoption-related events.
|
|
70
|
+
if ((packet.header.modelKey !== "event") || ((payload.type !== "deviceAdopted") && (payload.type !== "deviceUnadopted"))) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
// Make sure we have the right information to process the event.
|
|
74
|
+
if (!("deviceId" in payload.metadata) || !("text" in payload.metadata.deviceId)) {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
// Lookup the device.
|
|
78
|
+
const deviceId = payload.metadata.deviceId.text;
|
|
79
|
+
const protectDevice = this.nvr.deviceLookup(deviceId);
|
|
80
|
+
// We're adopting.
|
|
81
|
+
if (payload.type === "deviceAdopted") {
|
|
82
|
+
if (protectDevice) {
|
|
83
|
+
this.log.error("WE HAVE THE DEVICE ALREADY - WE ARE SCREWED!");
|
|
71
84
|
return;
|
|
72
85
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
86
|
+
this.nvr.addHomeKitDevice(this.ufpDeviceState[deviceId]);
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
// We're unadopting.
|
|
90
|
+
if (payload.type === "deviceUnadopted") {
|
|
91
|
+
// If it's already gone, we're done.
|
|
92
|
+
if (!protectDevice) {
|
|
79
93
|
return;
|
|
80
94
|
}
|
|
81
|
-
//
|
|
82
|
-
this.
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
this.nvrApi.eventsWs.once("close", () => {
|
|
86
|
-
var _a;
|
|
87
|
-
if (this.updatesListener) {
|
|
88
|
-
(_a = this.nvrApi.eventsWs) === null || _a === void 0 ? void 0 : _a.removeListener("message", this.updatesListener);
|
|
89
|
-
this.updatesListener = null;
|
|
90
|
-
}
|
|
91
|
-
});
|
|
92
|
-
return true;
|
|
95
|
+
// Remove the device.
|
|
96
|
+
this.nvr.removeHomeKitDevice(protectDevice);
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
93
99
|
}
|
|
94
|
-
//
|
|
95
|
-
|
|
100
|
+
// Listen to the UniFi Protect realtime updates API for updates we are interested in (e.g. motion).
|
|
101
|
+
configureEvents() {
|
|
96
102
|
// Only configure the event listener if it exists and it's not already configured.
|
|
97
|
-
if (
|
|
103
|
+
if (this.eventsHandler && this.ufpUpdatesHandler) {
|
|
98
104
|
return true;
|
|
99
105
|
}
|
|
100
|
-
//
|
|
101
|
-
this.
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
// id: "someCameraId"
|
|
112
|
-
// modelKey: "camera"
|
|
113
|
-
// newUpdateId: "ignorethis"
|
|
114
|
-
//
|
|
115
|
-
// The payloads are what differentiate them - one updates lastMotion and the other lastRing.
|
|
116
|
-
switch (updatePacket.action.modelKey) {
|
|
117
|
-
case "camera": {
|
|
118
|
-
// We listen for the following camera update actions:
|
|
119
|
-
// doorbell LCD updates
|
|
120
|
-
// doorbell rings
|
|
121
|
-
// motion detection
|
|
122
|
-
// We're only interested in update actions.
|
|
123
|
-
if (updatePacket.action.action !== "update") {
|
|
124
|
-
return;
|
|
125
|
-
}
|
|
126
|
-
// Grab the right payload type, camera update payloads.
|
|
127
|
-
const payload = updatePacket.payload;
|
|
128
|
-
// Now filter out payloads we aren't interested in. We only want motion detection and doorbell rings for now.
|
|
129
|
-
if (!payload.isMotionDetected && !payload.lastRing && !payload.lcdMessage &&
|
|
130
|
-
!payload.ledSettings && !payload.recordingSettings && !payload.state) {
|
|
131
|
-
return;
|
|
132
|
-
}
|
|
133
|
-
// Lookup the accessory associated with this device.
|
|
134
|
-
const accessory = this.nvr.accessoryLookup(updatePacket.action.id);
|
|
135
|
-
// We don't know about this device - we're done.
|
|
136
|
-
if (!accessory) {
|
|
137
|
-
return;
|
|
138
|
-
}
|
|
139
|
-
// Update the device JSON on the accessory.
|
|
140
|
-
accessory.context.device = Object.assign(accessory.context.device, payload);
|
|
141
|
-
// Grab the device context.
|
|
142
|
-
const device = accessory.context.device;
|
|
143
|
-
// Lookup the ProtectCamera instance associated with this accessory.
|
|
144
|
-
const protectCamera = this.nvr.configuredDevices[accessory.UUID];
|
|
145
|
-
if (!protectCamera) {
|
|
146
|
-
return;
|
|
147
|
-
}
|
|
148
|
-
// It's a motion event - process it accordingly.
|
|
149
|
-
if (payload.isMotionDetected) {
|
|
150
|
-
// We only want to process the motion event if we have the right payload, and either HKSV recording is enabled, or
|
|
151
|
-
// HKSV recording is disabled and we have smart motion events disabled since We handle those elsewhere.
|
|
152
|
-
if (payload.lastMotion &&
|
|
153
|
-
(((_a = protectCamera.stream.hksv) === null || _a === void 0 ? void 0 : _a.isRecording) || (!((_b = protectCamera.stream.hksv) === null || _b === void 0 ? void 0 : _b.isRecording) && !protectCamera.smartDetectTypes.length)) &&
|
|
154
|
-
this.nvr.optionEnabled(device, "Motion.NvrEvents", true)) {
|
|
155
|
-
this.motionEventHandler(accessory, payload.lastMotion);
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
// It's a ring event - process it accordingly.
|
|
159
|
-
if (payload.lastRing && this.nvr.optionEnabled(device, "Doorbell.NvrEvents", true)) {
|
|
160
|
-
this.doorbellEventHandler(accessory, payload.lastRing);
|
|
161
|
-
}
|
|
162
|
-
// It's a doorbell LCD message event - process it accordingly.
|
|
163
|
-
if (payload.lcdMessage) {
|
|
164
|
-
this.lcdMessageEventHandler(accessory, payload.lcdMessage);
|
|
165
|
-
}
|
|
166
|
-
// Process camera details updates:
|
|
167
|
-
// - camera status light.
|
|
168
|
-
// - camera recording settings.
|
|
169
|
-
if ((payload.ledSettings && ("isEnabled" in payload.ledSettings)) ||
|
|
170
|
-
(payload.recordingSettings && ("mode" in payload.recordingSettings)) || payload.recordingSettings) {
|
|
171
|
-
this.cameraDetailsHandler(accessory, protectCamera);
|
|
172
|
-
}
|
|
173
|
-
break;
|
|
174
|
-
}
|
|
175
|
-
case "event": {
|
|
176
|
-
// We listen for the following event actions:
|
|
177
|
-
// smart motion detection
|
|
178
|
-
// We're only interested in add events.
|
|
179
|
-
if (updatePacket.action.action !== "add") {
|
|
180
|
-
return;
|
|
181
|
-
}
|
|
182
|
-
// Grab the right payload type, for event add payloads.
|
|
183
|
-
const payload = updatePacket.payload;
|
|
184
|
-
// We're only interested in smart motion detection events.
|
|
185
|
-
if (payload.type !== "smartDetectZone") {
|
|
186
|
-
return;
|
|
187
|
-
}
|
|
188
|
-
// Lookup the accessory associated with this camera.
|
|
189
|
-
const accessory = this.nvr.accessoryLookup(payload.camera);
|
|
190
|
-
// We don't know about this camera - we're done.
|
|
191
|
-
if (!accessory) {
|
|
192
|
-
return;
|
|
193
|
-
}
|
|
194
|
-
// Grab the device context.
|
|
195
|
-
const device = accessory.context.device;
|
|
196
|
-
// Lookup the ProtectCamera instance associated with this accessory.
|
|
197
|
-
const protectCamera = this.nvr.configuredDevices[accessory.UUID];
|
|
198
|
-
if (!protectCamera) {
|
|
199
|
-
return;
|
|
200
|
-
}
|
|
201
|
-
// Process the motion event.
|
|
202
|
-
if (this.nvr.optionEnabled(device, "Motion.SmartDetect.NvrEvents", true)) {
|
|
203
|
-
this.motionEventHandler(accessory, payload.start, payload.smartDetectTypes);
|
|
106
|
+
// Ensure we update our UFP state before we process any other events.
|
|
107
|
+
this.prependListener("updateEvent", this.ufpUpdatesHandler = this.ufpUpdates.bind(this));
|
|
108
|
+
// Process remove events.
|
|
109
|
+
this.prependListener("addEvent", this.manageDevices.bind(this));
|
|
110
|
+
// Listen for any messages coming in from our listener. We route events to the appropriate handlers based on the type of event that comes across.
|
|
111
|
+
this.ufpApi.on("message", this.eventsHandler = (packet) => {
|
|
112
|
+
switch (packet.header.action) {
|
|
113
|
+
case "add":
|
|
114
|
+
this.emit("addEvent", packet);
|
|
115
|
+
if (packet.payload.camera) {
|
|
116
|
+
this.emit("addEvent." + packet.payload.camera, packet);
|
|
204
117
|
}
|
|
205
|
-
|
|
118
|
+
this.emit("addEvent." + packet.header.modelKey, packet);
|
|
206
119
|
break;
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
// brightness adjustments
|
|
212
|
-
// We're only interested in update actions.
|
|
213
|
-
if (updatePacket.action.action !== "update") {
|
|
214
|
-
return;
|
|
215
|
-
}
|
|
216
|
-
// Grab the right payload type, camera update payloads.
|
|
217
|
-
const payload = updatePacket.payload;
|
|
218
|
-
// Now filter out payloads we aren't interested in. We only want light state, brightness, and motion detection.
|
|
219
|
-
if (!payload.isPirMotionDetected && !payload.isLightOn && !payload.lightDeviceSettings) {
|
|
220
|
-
return;
|
|
221
|
-
}
|
|
222
|
-
// Lookup the accessory associated with this device.
|
|
223
|
-
const accessory = this.nvr.accessoryLookup(updatePacket.action.id);
|
|
224
|
-
// We don't know about this device - we're done.
|
|
225
|
-
if (!accessory) {
|
|
226
|
-
return;
|
|
227
|
-
}
|
|
228
|
-
// Grab the device context.
|
|
229
|
-
const device = accessory.context.device;
|
|
230
|
-
// Lookup the ProtectCamera instance associated with this accessory.
|
|
231
|
-
const protectLight = this.nvr.configuredDevices[accessory.UUID];
|
|
232
|
-
if (!protectLight) {
|
|
233
|
-
return;
|
|
234
|
-
}
|
|
235
|
-
// It's a motion event - process it accordingly.
|
|
236
|
-
if (payload.isPirMotionDetected && payload.lastMotion && this.nvr.optionEnabled(device, "Motion.NvrEvents", true)) {
|
|
237
|
-
this.motionEventHandler(accessory, payload.lastMotion);
|
|
238
|
-
}
|
|
239
|
-
// It's a light power event - process it accordingly.
|
|
240
|
-
if (payload.isLightOn) {
|
|
241
|
-
this.lightPowerHandler(accessory, payload.isLightOn);
|
|
242
|
-
}
|
|
243
|
-
// It's light brightness event - process it accordingly.
|
|
244
|
-
if ((_c = payload.lightDeviceSettings) === null || _c === void 0 ? void 0 : _c.ledLevel) {
|
|
245
|
-
this.lightBrightnessHandler(accessory, payload.lightDeviceSettings.ledLevel);
|
|
246
|
-
}
|
|
247
|
-
break;
|
|
248
|
-
}
|
|
249
|
-
case "nvr": {
|
|
250
|
-
// We listen for the following sensor update actions:
|
|
251
|
-
// motion events
|
|
252
|
-
// sensor enablement / configuration changes
|
|
253
|
-
// sensor updates (humidity, light, temperature)
|
|
254
|
-
// We're only interested in update actions.
|
|
255
|
-
if (updatePacket.action.action !== "update") {
|
|
256
|
-
return;
|
|
257
|
-
}
|
|
258
|
-
// Grab the right payload type.
|
|
259
|
-
const payload = updatePacket.payload;
|
|
260
|
-
// Now filter out payloads we aren't interested in. We only want NVR system information updates.
|
|
261
|
-
if (!("systemInfo" in payload)) {
|
|
262
|
-
return;
|
|
263
|
-
}
|
|
264
|
-
// Process it.
|
|
265
|
-
(_d = this.nvr.systemInfo) === null || _d === void 0 ? void 0 : _d.updateDevice(false, payload.systemInfo);
|
|
120
|
+
case "remove":
|
|
121
|
+
this.emit("removeEvent", packet);
|
|
122
|
+
this.emit("removeEvent." + packet.header.id, packet);
|
|
123
|
+
this.emit("removeEvent." + packet.header.modelKey, packet);
|
|
266
124
|
break;
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
// sensor enablement / configuration changes
|
|
272
|
-
// sensor updates (humidity, light, temperature)
|
|
273
|
-
// We're only interested in update actions.
|
|
274
|
-
if (updatePacket.action.action !== "update") {
|
|
275
|
-
return;
|
|
276
|
-
}
|
|
277
|
-
// Grab the right payload type.
|
|
278
|
-
const payload = updatePacket.payload;
|
|
279
|
-
// Now filter out payloads we aren't interested in. We only want motion events, stats updates, and changes in sensor configuration.
|
|
280
|
-
if (!("isMotionDetected" in payload) && !("isOpened" in payload) && !("stats" in payload) &&
|
|
281
|
-
!("mountType" in payload) && !("alarmSettings" in payload) && !("humiditySettings" in payload) &&
|
|
282
|
-
!("lightSettings" in payload) && !("motionSettings" in payload) && !("temperatureSettings" in payload) &&
|
|
283
|
-
!("batteryStatus" in payload) && !("tamperingDetectedAt" in payload) && !("alarmTriggeredAt" in payload) &&
|
|
284
|
-
!("state" in payload)) {
|
|
285
|
-
return;
|
|
286
|
-
}
|
|
287
|
-
// Lookup the accessory associated with this device.
|
|
288
|
-
const accessory = this.nvr.accessoryLookup(updatePacket.action.id);
|
|
289
|
-
// We don't know about this device - we're done.
|
|
290
|
-
if (!accessory) {
|
|
291
|
-
return;
|
|
292
|
-
}
|
|
293
|
-
// Update the device JSON on the accessory.
|
|
294
|
-
accessory.context.device = Object.assign(accessory.context.device, payload);
|
|
295
|
-
// Grab the device context.
|
|
296
|
-
const device = accessory.context.device;
|
|
297
|
-
// Lookup the ProtectSensor instance associated with this accessory.
|
|
298
|
-
const protectSensor = this.nvr.configuredDevices[accessory.UUID];
|
|
299
|
-
if (!protectSensor) {
|
|
300
|
-
return;
|
|
301
|
-
}
|
|
302
|
-
// It's a motion event - process it accordingly.
|
|
303
|
-
if (payload.isMotionDetected && payload.motionDetectedAt && this.nvr.optionEnabled(device, "Motion.NvrEvents", true)) {
|
|
304
|
-
this.motionEventHandler(accessory, payload.motionDetectedAt);
|
|
305
|
-
}
|
|
306
|
-
// Process it.
|
|
307
|
-
this.sensorHandler(accessory, protectSensor);
|
|
125
|
+
case "update":
|
|
126
|
+
this.emit("updateEvent", packet);
|
|
127
|
+
this.emit("updateEvent." + packet.header.id, packet);
|
|
128
|
+
this.emit("updateEvent." + packet.header.modelKey, packet);
|
|
308
129
|
break;
|
|
309
|
-
}
|
|
310
130
|
default:
|
|
311
|
-
// It's not a modelKey we're interested in. We're done.
|
|
312
|
-
return;
|
|
313
131
|
break;
|
|
314
132
|
}
|
|
315
133
|
});
|
|
316
|
-
// Cleanup after ourselves.
|
|
317
|
-
this.nvrApi.eventsWs.once("close", () => {
|
|
318
|
-
var _a;
|
|
319
|
-
if (this.updatesListener) {
|
|
320
|
-
(_a = this.nvrApi.eventsWs) === null || _a === void 0 ? void 0 : _a.removeListener("message", this.updatesListener);
|
|
321
|
-
this.updatesListener = null;
|
|
322
|
-
this.log.error("%s: UniFi Protect realtime events API has been closed. This is usually due to a controller restart or disconnect.", this.name());
|
|
323
|
-
}
|
|
324
|
-
});
|
|
325
134
|
return true;
|
|
326
135
|
}
|
|
327
|
-
// Camera details event processing from UniFi Protect for state-specific information.
|
|
328
|
-
cameraDetailsHandler(accessory, protectCamera) {
|
|
329
|
-
// Update the camera details in HomeKit.
|
|
330
|
-
protectCamera.updateDevice();
|
|
331
|
-
}
|
|
332
136
|
// Motion event processing from UniFi Protect.
|
|
333
|
-
motionEventHandler(
|
|
334
|
-
|
|
335
|
-
if (!device || !lastMotion) {
|
|
137
|
+
motionEventHandler(protectDevice, lastMotion, detectedObjects = []) {
|
|
138
|
+
if (!protectDevice || !lastMotion) {
|
|
336
139
|
return;
|
|
337
140
|
}
|
|
338
141
|
// Have we seen this event before? If so...move along.
|
|
339
|
-
if (this.lastMotion[
|
|
340
|
-
this.debug("
|
|
341
|
-
return;
|
|
342
|
-
}
|
|
343
|
-
// We only consider events that have happened within the last two refresh intervals. Otherwise, we assume
|
|
344
|
-
// it's stale data and don't inform the user.
|
|
345
|
-
if ((Date.now() - lastMotion) > (this.nvr.refreshInterval * 2 * 1000)) {
|
|
346
|
-
this.debug("%s: Skipping motion event due to stale data.", this.nvrApi.getFullName(device));
|
|
142
|
+
if (this.lastMotion[protectDevice.ufp.mac] >= lastMotion) {
|
|
143
|
+
this.log.debug("Skipping duplicate motion event.");
|
|
347
144
|
return;
|
|
348
145
|
}
|
|
349
146
|
// Remember this event.
|
|
350
|
-
this.lastMotion[
|
|
147
|
+
this.lastMotion[protectDevice.ufp.mac] = lastMotion;
|
|
351
148
|
// If we already have a motion event inflight, allow it to complete so we don't spam users.
|
|
352
|
-
if (this.eventTimers[
|
|
149
|
+
if (this.eventTimers[protectDevice.ufp.mac]) {
|
|
353
150
|
return;
|
|
354
151
|
}
|
|
355
152
|
// Only notify the user if we have a motion sensor and it's active.
|
|
356
|
-
const motionService = accessory.getService(this.hap.Service.MotionSensor);
|
|
153
|
+
const motionService = protectDevice.accessory.getService(this.hap.Service.MotionSensor);
|
|
357
154
|
if (motionService) {
|
|
358
|
-
this.motionEventDelivery(
|
|
155
|
+
this.motionEventDelivery(protectDevice, motionService, detectedObjects);
|
|
359
156
|
}
|
|
360
157
|
}
|
|
361
158
|
// Motion event delivery to HomeKit.
|
|
362
|
-
motionEventDelivery(
|
|
363
|
-
|
|
364
|
-
const device = accessory.context.device;
|
|
365
|
-
// Lookup the ProtectCamera instance associated with this accessory.
|
|
366
|
-
const protectCamera = this.nvr.configuredDevices[accessory.UUID];
|
|
367
|
-
if (!protectCamera) {
|
|
159
|
+
motionEventDelivery(protectDevice, motionService, detectedObjects = []) {
|
|
160
|
+
if (!protectDevice) {
|
|
368
161
|
return;
|
|
369
162
|
}
|
|
370
163
|
// If we have disabled motion events, we're done here.
|
|
371
|
-
if (("detectMotion" in accessory.context) && !accessory.context.detectMotion) {
|
|
164
|
+
if (("detectMotion" in protectDevice.accessory.context) && !protectDevice.accessory.context.detectMotion) {
|
|
372
165
|
return;
|
|
373
166
|
}
|
|
374
|
-
|
|
375
|
-
//
|
|
376
|
-
|
|
377
|
-
//
|
|
378
|
-
|
|
379
|
-
if (
|
|
380
|
-
(
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
(_c = this.nvr.mqtt) === null || _c === void 0 ? void 0 : _c.publish(accessory, "motion", "true");
|
|
390
|
-
// Log the event, if configured to do so.
|
|
391
|
-
if (this.nvr.optionEnabled(device, "Log.Motion", false)) {
|
|
392
|
-
this.log.info("%s: Motion detected%s.", this.nvrApi.getFullName(device), ((protectCamera instanceof protect_camera_1.ProtectCamera) && !((_d = protectCamera.stream.hksv) === null || _d === void 0 ? void 0 : _d.isRecording) && detectedObjects.length) ? ": " + detectedObjects.join(", ") : "");
|
|
393
|
-
}
|
|
167
|
+
const protectCamera = protectDevice;
|
|
168
|
+
// Trigger the motion event in HomeKit.
|
|
169
|
+
motionService.updateCharacteristic(this.hap.Characteristic.MotionDetected, true);
|
|
170
|
+
// Check to see if we have a motion trigger switch configured. If we do, update it.
|
|
171
|
+
const triggerService = protectDevice.accessory.getServiceById(this.hap.Service.Switch, ProtectReservedNames.SWITCH_MOTION_TRIGGER);
|
|
172
|
+
if (triggerService) {
|
|
173
|
+
triggerService.updateCharacteristic(this.hap.Characteristic.On, true);
|
|
174
|
+
}
|
|
175
|
+
// Publish the motion event to MQTT, if the user has configured it.
|
|
176
|
+
this.nvr.mqtt?.publish(protectDevice.accessory, "motion", "true");
|
|
177
|
+
// Log the event, if configured to do so.
|
|
178
|
+
if (protectDevice.hints.logMotion) {
|
|
179
|
+
protectDevice.log.info("Motion detected%s.", ((protectDevice.ufp.modelKey === "camera") && detectedObjects.length &&
|
|
180
|
+
(!protectCamera.stream.hksv?.isRecording || this.nvr.optionEnabled(protectDevice.ufp, "Motion.SmartDetect.ObjectSensors", false)))
|
|
181
|
+
? ": " + detectedObjects.join(", ") : "");
|
|
394
182
|
}
|
|
395
183
|
// Trigger smart motion contact sensors, if configured.
|
|
396
184
|
for (const detectedObject of detectedObjects) {
|
|
397
|
-
const contactService = accessory.getServiceById(this.hap.Service.ContactSensor,
|
|
185
|
+
const contactService = protectDevice.accessory.getServiceById(this.hap.Service.ContactSensor, ProtectReservedNames.CONTACT_MOTION_SMARTDETECT + "." + detectedObject);
|
|
398
186
|
if (contactService) {
|
|
399
187
|
contactService.updateCharacteristic(this.hap.Characteristic.ContactSensorState, true);
|
|
400
188
|
}
|
|
401
189
|
// Publish the smart motion event to MQTT, if the user has configured it.
|
|
402
|
-
|
|
190
|
+
this.nvr.mqtt?.publish(protectDevice.accessory, "motion/smart/" + detectedObject, "true");
|
|
403
191
|
}
|
|
404
192
|
// Reset our motion event after motionDuration if we don't already have a reset timer inflight.
|
|
405
|
-
if (!this.eventTimers[
|
|
406
|
-
this.eventTimers[
|
|
407
|
-
|
|
408
|
-
const thisMotionService = accessory.getService(this.hap.Service.MotionSensor);
|
|
193
|
+
if (!this.eventTimers[protectDevice.ufp.mac]) {
|
|
194
|
+
this.eventTimers[protectDevice.ufp.mac] = setTimeout(() => {
|
|
195
|
+
const thisMotionService = protectDevice.accessory.getService(this.hap.Service.MotionSensor);
|
|
409
196
|
if (thisMotionService) {
|
|
410
197
|
thisMotionService.updateCharacteristic(this.hap.Characteristic.MotionDetected, false);
|
|
411
198
|
// Check to see if we have a motion trigger switch configured. If we do, update it.
|
|
412
|
-
const thisTriggerService = accessory.getServiceById(this.hap.Service.Switch,
|
|
199
|
+
const thisTriggerService = protectDevice.accessory.getServiceById(this.hap.Service.Switch, ProtectReservedNames.SWITCH_MOTION_TRIGGER);
|
|
413
200
|
if (thisTriggerService) {
|
|
414
201
|
thisTriggerService.updateCharacteristic(this.hap.Characteristic.On, false);
|
|
415
202
|
}
|
|
416
|
-
this.debug("
|
|
203
|
+
this.log.debug("Resetting motion event.");
|
|
417
204
|
}
|
|
418
205
|
// Publish to MQTT, if the user has configured it.
|
|
419
|
-
|
|
206
|
+
this.nvr.mqtt?.publish(protectDevice.accessory, "motion", "false");
|
|
420
207
|
// Delete the timer from our motion event tracker.
|
|
421
|
-
delete this.eventTimers[
|
|
208
|
+
delete this.eventTimers[protectDevice.ufp.mac];
|
|
422
209
|
}, this.motionDuration * 1000);
|
|
423
210
|
}
|
|
424
211
|
// Reset our smart motion contact sensors after motionDuration.
|
|
425
|
-
if (!this.eventTimers[
|
|
426
|
-
this.eventTimers[
|
|
427
|
-
var _a;
|
|
212
|
+
if (!this.eventTimers[protectDevice.ufp.mac + ".Motion.SmartDetect.ObjectSensors"]) {
|
|
213
|
+
this.eventTimers[protectDevice.ufp.mac + ".Motion.SmartDetect.ObjectSensors"] = setTimeout(() => {
|
|
428
214
|
// Reset smart motion contact sensors, if configured.
|
|
429
215
|
for (const detectedObject of detectedObjects) {
|
|
430
|
-
const contactService = accessory.getServiceById(this.hap.Service.ContactSensor,
|
|
216
|
+
const contactService = protectDevice.accessory.getServiceById(this.hap.Service.ContactSensor, ProtectReservedNames.CONTACT_MOTION_SMARTDETECT + "." + detectedObject);
|
|
431
217
|
if (contactService) {
|
|
432
218
|
contactService.updateCharacteristic(this.hap.Characteristic.ContactSensorState, false);
|
|
433
219
|
}
|
|
434
220
|
// Publish the smart motion event to MQTT, if the user has configured it.
|
|
435
|
-
|
|
436
|
-
this.debug("
|
|
221
|
+
this.nvr.mqtt?.publish(protectDevice.accessory, "motion/smart/" + detectedObject, "false");
|
|
222
|
+
this.log.debug("Resetting smart object motion event.");
|
|
437
223
|
}
|
|
438
224
|
// Delete the timer from our motion event tracker.
|
|
439
|
-
delete this.eventTimers[
|
|
225
|
+
delete this.eventTimers[protectDevice.ufp.mac + ".Motion.SmartDetect.ObjectSensors"];
|
|
440
226
|
}, this.motionDuration * 1000);
|
|
441
227
|
}
|
|
442
228
|
}
|
|
443
229
|
// Doorbell event processing from UniFi Protect and delivered to HomeKit.
|
|
444
|
-
doorbellEventHandler(
|
|
445
|
-
|
|
446
|
-
const device = accessory.context.device;
|
|
447
|
-
if (!device || !lastRing) {
|
|
230
|
+
doorbellEventHandler(protectDevice, lastRing) {
|
|
231
|
+
if (!protectDevice || !lastRing) {
|
|
448
232
|
return;
|
|
449
233
|
}
|
|
450
234
|
// Have we seen this event before? If so...move along. It's unlikely we hit this in a doorbell scenario, but just in case.
|
|
451
|
-
if (this.lastRing[
|
|
452
|
-
this.debug("
|
|
453
|
-
return;
|
|
454
|
-
}
|
|
455
|
-
// We only consider events that have happened within the last two refresh intervals. Otherwise, we assume it's stale
|
|
456
|
-
// data and don't inform the user.
|
|
457
|
-
if ((Date.now() - lastRing) > (this.nvr.refreshInterval * 2 * 1000)) {
|
|
458
|
-
this.debug("%s: Skipping doorbell ring due to stale data.", this.nvrApi.getFullName(device));
|
|
235
|
+
if (this.lastRing[protectDevice.ufp.mac] >= lastRing) {
|
|
236
|
+
this.log.debug("Skipping duplicate doorbell ring.");
|
|
459
237
|
return;
|
|
460
238
|
}
|
|
461
239
|
// Remember this event.
|
|
462
|
-
this.lastRing[
|
|
240
|
+
this.lastRing[protectDevice.ufp.mac] = lastRing;
|
|
463
241
|
// Only notify the user if we have a doorbell.
|
|
464
|
-
const doorbellService = accessory.getService(this.hap.Service.Doorbell);
|
|
242
|
+
const doorbellService = protectDevice.accessory.getService(this.hap.Service.Doorbell);
|
|
465
243
|
if (!doorbellService) {
|
|
466
244
|
return;
|
|
467
245
|
}
|
|
468
246
|
// Trigger the doorbell. We delay this slightly to workaround what appears to be a race
|
|
469
247
|
// condition bug in HomeKit. Inelegant, but effective.
|
|
470
248
|
setTimeout(() => {
|
|
471
|
-
|
|
472
|
-
|
|
249
|
+
doorbellService.getCharacteristic(this.hap.Characteristic.ProgrammableSwitchEvent)
|
|
250
|
+
?.sendEventNotification(this.hap.Characteristic.ProgrammableSwitchEvent.SINGLE_PRESS);
|
|
473
251
|
}, 500);
|
|
474
252
|
// Check to see if we have a doorbell trigger switch configured. If we do, update it.
|
|
475
|
-
const triggerService = accessory.getServiceById(this.hap.Service.Switch,
|
|
253
|
+
const triggerService = protectDevice.accessory.getServiceById(this.hap.Service.Switch, ProtectReservedNames.SWITCH_DOORBELL_TRIGGER);
|
|
476
254
|
if (triggerService) {
|
|
477
255
|
// Kill any inflight trigger reset.
|
|
478
|
-
if (this.eventTimers[
|
|
479
|
-
clearTimeout(this.eventTimers[
|
|
480
|
-
delete this.eventTimers[
|
|
256
|
+
if (this.eventTimers[protectDevice.ufp.mac + ".Doorbell.Ring.Trigger"]) {
|
|
257
|
+
clearTimeout(this.eventTimers[protectDevice.ufp.mac + ".Doorbell.Ring.Trigger"]);
|
|
258
|
+
delete this.eventTimers[protectDevice.ufp.mac + ".Doorbell.Ring.Trigger"];
|
|
481
259
|
}
|
|
482
|
-
const protectCamera = this.nvr.configuredDevices[accessory.UUID];
|
|
483
260
|
// Flag that we're ringing.
|
|
484
|
-
|
|
485
|
-
protectCamera.isRinging = true;
|
|
486
|
-
}
|
|
261
|
+
protectDevice.isRinging = true;
|
|
487
262
|
// Update the trigger switch state.
|
|
488
263
|
triggerService.updateCharacteristic(this.hap.Characteristic.On, true);
|
|
489
264
|
// Reset our doorbell trigger after ringDuration.
|
|
490
|
-
this.eventTimers[
|
|
491
|
-
|
|
492
|
-
protectCamera.isRinging = false;
|
|
493
|
-
}
|
|
265
|
+
this.eventTimers[protectDevice.ufp.mac + ".Doorbell.Ring.Trigger"] = setTimeout(() => {
|
|
266
|
+
protectDevice.isRinging = false;
|
|
494
267
|
triggerService.updateCharacteristic(this.hap.Characteristic.On, false);
|
|
495
|
-
this.debug("
|
|
268
|
+
this.log.debug("Resetting doorbell ring trigger.");
|
|
496
269
|
// Delete the timer from our motion event tracker.
|
|
497
|
-
delete this.eventTimers[
|
|
270
|
+
delete this.eventTimers[protectDevice.ufp.mac + ".Doorbell.Ring.Trigger"];
|
|
498
271
|
}, this.ringDuration * 1000);
|
|
499
272
|
}
|
|
500
273
|
// Publish to MQTT, if the user has configured it.
|
|
501
|
-
|
|
502
|
-
if (
|
|
503
|
-
|
|
274
|
+
this.nvr.mqtt?.publish(protectDevice.accessory, "doorbell", "true");
|
|
275
|
+
if (protectDevice.hints.logDoorbell) {
|
|
276
|
+
protectDevice.log.info("Doorbell ring detected.");
|
|
504
277
|
}
|
|
505
278
|
// Kill any inflight ring reset.
|
|
506
|
-
if (this.eventTimers[
|
|
507
|
-
clearTimeout(this.eventTimers[
|
|
508
|
-
delete this.eventTimers[
|
|
279
|
+
if (this.eventTimers[protectDevice.ufp.mac + ".Doorbell.Ring"]) {
|
|
280
|
+
clearTimeout(this.eventTimers[protectDevice.ufp.mac + ".Doorbell.Ring"]);
|
|
281
|
+
delete this.eventTimers[protectDevice.ufp.mac + ".Doorbell.Ring"];
|
|
509
282
|
}
|
|
510
283
|
// Fire off our MQTT doorbell ring event after ringDuration.
|
|
511
|
-
this.eventTimers[
|
|
512
|
-
|
|
513
|
-
(_a = this.nvr.mqtt) === null || _a === void 0 ? void 0 : _a.publish(accessory, "doorbell", "false");
|
|
284
|
+
this.eventTimers[protectDevice.ufp.mac + ".Doorbell.Ring"] = setTimeout(() => {
|
|
285
|
+
this.nvr.mqtt?.publish(protectDevice.accessory, "doorbell", "false");
|
|
514
286
|
// Delete the timer from our event tracker.
|
|
515
|
-
delete this.eventTimers[
|
|
287
|
+
delete this.eventTimers[protectDevice.ufp.mac + ".Doorbell.Ring"];
|
|
516
288
|
}, this.ringDuration * 1000);
|
|
517
289
|
}
|
|
518
|
-
// LCD message event processing from UniFi Protect and delivered to HomeKit.
|
|
519
|
-
lcdMessageEventHandler(accessory, lcdMessage) {
|
|
520
|
-
var _a;
|
|
521
|
-
const device = accessory.context.device;
|
|
522
|
-
if (!device) {
|
|
523
|
-
return;
|
|
524
|
-
}
|
|
525
|
-
(_a = this.nvr.configuredDevices[accessory.UUID]) === null || _a === void 0 ? void 0 : _a.updateLcdSwitch(lcdMessage);
|
|
526
|
-
}
|
|
527
|
-
// Light power state event processing from UniFi Protect.
|
|
528
|
-
lightPowerHandler(accessory, lightState) {
|
|
529
|
-
const device = accessory.context.device;
|
|
530
|
-
if (!device) {
|
|
531
|
-
return;
|
|
532
|
-
}
|
|
533
|
-
// Update the power state on the accessory.
|
|
534
|
-
const lightService = accessory.getService(this.hap.Service.Lightbulb);
|
|
535
|
-
lightService === null || lightService === void 0 ? void 0 : lightService.updateCharacteristic(this.hap.Characteristic.On, lightState);
|
|
536
|
-
}
|
|
537
|
-
// Light power state event processing from UniFi Protect.
|
|
538
|
-
lightBrightnessHandler(accessory, brightness) {
|
|
539
|
-
const device = accessory.context.device;
|
|
540
|
-
if (!device || (brightness < 1)) {
|
|
541
|
-
return;
|
|
542
|
-
}
|
|
543
|
-
// Update the power state on the accessory.
|
|
544
|
-
const lightService = accessory.getService(this.hap.Service.Lightbulb);
|
|
545
|
-
lightService === null || lightService === void 0 ? void 0 : lightService.updateCharacteristic(this.hap.Characteristic.Brightness, (brightness - 1) * 20);
|
|
546
|
-
}
|
|
547
|
-
// Sensor state event processing from UniFi Protect.
|
|
548
|
-
sensorHandler(accessory, protectSensor) {
|
|
549
|
-
// Update the sensor state in HomeKit.
|
|
550
|
-
protectSensor.updateDevice();
|
|
551
|
-
}
|
|
552
290
|
}
|
|
553
|
-
exports.ProtectNvrEvents = ProtectNvrEvents;
|
|
554
291
|
//# sourceMappingURL=protect-nvr-events.js.map
|