homebridge-unifi-protect 4.4.1 → 5.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 +8 -12
- package/config.schema.json +11 -0
- package/dist/index.js +1 -1
- package/dist/protect-accessory.js +145 -2
- package/dist/protect-accessory.js.map +1 -1
- package/dist/protect-camera.js +56 -162
- package/dist/protect-camera.js.map +1 -1
- package/dist/protect-doorbell.js +14 -26
- package/dist/protect-doorbell.js.map +1 -1
- package/dist/protect-ffmpeg.js +24 -5
- package/dist/protect-ffmpeg.js.map +1 -1
- package/dist/protect-light.js +111 -0
- package/dist/protect-light.js.map +1 -0
- package/dist/protect-liveviews.js +2 -2
- package/dist/protect-liveviews.js.map +1 -1
- package/dist/protect-mqtt.js +47 -19
- package/dist/protect-mqtt.js.map +1 -1
- package/dist/protect-nvr-events.js +206 -61
- package/dist/protect-nvr-events.js.map +1 -1
- package/dist/protect-nvr-systeminfo.js +174 -0
- package/dist/protect-nvr-systeminfo.js.map +1 -0
- package/dist/protect-nvr.js +167 -61
- package/dist/protect-nvr.js.map +1 -1
- package/dist/protect-options.js +7 -0
- package/dist/protect-options.js.map +1 -0
- package/dist/protect-platform.js +8 -3
- package/dist/protect-platform.js.map +1 -1
- package/dist/protect-rtp.js +1 -1
- package/dist/protect-rtp.js.map +1 -1
- package/dist/protect-securitysystem.js +3 -4
- package/dist/protect-securitysystem.js.map +1 -1
- package/dist/protect-sensor.js +357 -0
- package/dist/protect-sensor.js.map +1 -0
- package/dist/protect-stream.js +54 -19
- package/dist/protect-stream.js.map +1 -1
- package/dist/protect-viewer.js +170 -0
- package/dist/protect-viewer.js.map +1 -0
- package/dist/settings.js +6 -2
- package/dist/settings.js.map +1 -1
- package/package.json +16 -18
- package/dist/protect-api-updates.js +0 -166
- package/dist/protect-api-updates.js.map +0 -1
- package/dist/protect-api.js +0 -555
- package/dist/protect-api.js.map +0 -1
- package/dist/protect-types.js +0 -3
- package/dist/protect-types.js.map +0 -1
|
@@ -1,166 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.ProtectApiUpdates = void 0;
|
|
7
|
-
const zlib_1 = __importDefault(require("zlib"));
|
|
8
|
-
/*
|
|
9
|
-
* The UniFi Protect realtime updates API is largely undocumented and has been reverse engineered mostly through
|
|
10
|
-
* trial and error, as well as observing the Protect controller in action.
|
|
11
|
-
*
|
|
12
|
-
* Here's how to get started with the UniFi Protect Updates API:
|
|
13
|
-
*
|
|
14
|
-
* 1. Login to the UniFi Protect controller, obtain the bootstrap JSON.
|
|
15
|
-
* 2. Open the websocket to the updates URL (see protect-api.ts).
|
|
16
|
-
*
|
|
17
|
-
* Then you're ready to listen to messages. You can see an example of this in protect-nvr.ts.
|
|
18
|
-
*
|
|
19
|
-
* Those are the basics and gets us up and running. Now, to explain how the updates API works...
|
|
20
|
-
*
|
|
21
|
-
* UniFi OS update data packets are used to provide a realtime stream of updates to Protect. It differs from
|
|
22
|
-
* the system events API in that the system events API appears to be shared across other applications (Network, Access, etc.)
|
|
23
|
-
* while the updates events API appears to only be utilized by Protect and not shared by other applications, although the protocol
|
|
24
|
-
* is shared.
|
|
25
|
-
*
|
|
26
|
-
* So how does it all work? Cameras continuously stream updates to the UniFi Protect controller containing things like camera
|
|
27
|
-
* health, statistics, and - crucially for us - events such as motion and doorbell ring. A complete update packet is composed of four
|
|
28
|
-
* frames:
|
|
29
|
-
*
|
|
30
|
-
* Header Frame (8 bytes)
|
|
31
|
-
* Action Frame
|
|
32
|
-
* Header Frame (8 bytes)
|
|
33
|
-
* Data Frame
|
|
34
|
-
*
|
|
35
|
-
* The header frame is required overhead since websockets provide only a transport medium. It's purpose is to tell us what's
|
|
36
|
-
* coming in the frame that follows.
|
|
37
|
-
*
|
|
38
|
-
* The action frame identifies what the action and category that the update contains:
|
|
39
|
-
*
|
|
40
|
-
* Property Description
|
|
41
|
-
* -------- -----------
|
|
42
|
-
* action What action is being taken. Known actions are "add" and "update".
|
|
43
|
-
* id The identifier for the device we're updating.
|
|
44
|
-
* modelKey The device model category that we're updating.
|
|
45
|
-
* newUpdateId A new UUID generated on a per-update basis. This can be safely ignored it seems.
|
|
46
|
-
*
|
|
47
|
-
* The final part of the update packet is the data frame. The data frame can be three different types of data - although in
|
|
48
|
-
* practice, I've only seen JSONs come across. Those types are:
|
|
49
|
-
*
|
|
50
|
-
* Payload Type Description
|
|
51
|
-
* 1 JSON. For update actions that are not events, this is always a subset of the configuration bootstrap JSON.
|
|
52
|
-
* 2 A UTF8-encoded string
|
|
53
|
-
* 3 Node Buffer
|
|
54
|
-
*
|
|
55
|
-
* Some tips:
|
|
56
|
-
*
|
|
57
|
-
* - "update" actions are always tied to the following modelKeys: camera, event, nvr, and user.
|
|
58
|
-
*
|
|
59
|
-
* - "add" actions are always tied to the "event" modelKey and indicate the beginning of an event item in the Protect events list.
|
|
60
|
-
* A subsequent "update" action is sent signaling the end of the event capture, and it's confidence score for motion detection.
|
|
61
|
-
*
|
|
62
|
-
* - The above is NOT the same thing as motion detection. If you want to detect motion, you should watch the "update" action for "camera"
|
|
63
|
-
* modelKeys, and look for a JSON that updates lastMotion. For doorbell rings, lastRing. The Protect events list is useful for the
|
|
64
|
-
* Protect app, but it's of limited utility to HomeKit, and it's slow - relative to looking for lastMotion that is. If you want true
|
|
65
|
-
* realtime updates, you want to look at the "update" action.
|
|
66
|
-
*
|
|
67
|
-
* - JSONs are only payload type that seems to be sent, although the protocol is designed to accept all three.
|
|
68
|
-
*
|
|
69
|
-
* - With the exception of update actions with a modelKey of event, JSONs are always a subset of the bootstrap JSON, indexed off
|
|
70
|
-
* of modelKey. So for a modelKey of camera, the data payload is always a subset of ProtectCameraConfigInterface (see protect-types.ts).
|
|
71
|
-
*/
|
|
72
|
-
// Update realtime API packet header size, in bytes.
|
|
73
|
-
const UPDATE_PACKET_HEADER_SIZE = 8;
|
|
74
|
-
// Update realtime API packet types.
|
|
75
|
-
var UpdatePacketType;
|
|
76
|
-
(function (UpdatePacketType) {
|
|
77
|
-
UpdatePacketType[UpdatePacketType["ACTION"] = 1] = "ACTION";
|
|
78
|
-
UpdatePacketType[UpdatePacketType["PAYLOAD"] = 2] = "PAYLOAD";
|
|
79
|
-
})(UpdatePacketType || (UpdatePacketType = {}));
|
|
80
|
-
// Update realtime API payload types.
|
|
81
|
-
var UpdatePayloadType;
|
|
82
|
-
(function (UpdatePayloadType) {
|
|
83
|
-
UpdatePayloadType[UpdatePayloadType["JSON"] = 1] = "JSON";
|
|
84
|
-
UpdatePayloadType[UpdatePayloadType["STRING"] = 2] = "STRING";
|
|
85
|
-
UpdatePayloadType[UpdatePayloadType["BUFFER"] = 3] = "BUFFER";
|
|
86
|
-
})(UpdatePayloadType || (UpdatePayloadType = {}));
|
|
87
|
-
/* A packet header is composed of 8 bytes in this order:
|
|
88
|
-
*
|
|
89
|
-
* Byte Offset Description Bits Values
|
|
90
|
-
* 0 Packet Type 8 1 - action frame, 2 - payload frame.
|
|
91
|
-
* 1 Payload Format 8 1 - JSON object, 2 - UTF8-encoded string, 3 - Node Buffer.
|
|
92
|
-
* 2 Deflated 8 0 - uncompressed, 1 - compressed / deflated (zlib-based compression).
|
|
93
|
-
* 3 Unknown 8 Always 0. Possibly reserved for future use by Ubiquiti?
|
|
94
|
-
* 4-7 Payload Size: 32 Size of payload in network-byte order (big endian).
|
|
95
|
-
*/
|
|
96
|
-
var UpdatePacketHeader;
|
|
97
|
-
(function (UpdatePacketHeader) {
|
|
98
|
-
UpdatePacketHeader[UpdatePacketHeader["TYPE"] = 0] = "TYPE";
|
|
99
|
-
UpdatePacketHeader[UpdatePacketHeader["PAYLOAD_FORMAT"] = 1] = "PAYLOAD_FORMAT";
|
|
100
|
-
UpdatePacketHeader[UpdatePacketHeader["DEFLATED"] = 2] = "DEFLATED";
|
|
101
|
-
UpdatePacketHeader[UpdatePacketHeader["UNKNOWN"] = 3] = "UNKNOWN";
|
|
102
|
-
UpdatePacketHeader[UpdatePacketHeader["PAYLOAD_SIZE"] = 4] = "PAYLOAD_SIZE";
|
|
103
|
-
})(UpdatePacketHeader || (UpdatePacketHeader = {}));
|
|
104
|
-
class ProtectApiUpdates {
|
|
105
|
-
// Process an update data packet and return the action and payload.
|
|
106
|
-
static decodeUpdatePacket(log, packet) {
|
|
107
|
-
// What we need to do here is to split this packet into the header and payload, and decode them.
|
|
108
|
-
let dataOffset;
|
|
109
|
-
try {
|
|
110
|
-
// The fourth byte holds our payload size. When you add the payload size to our header frame size, you get the location of the
|
|
111
|
-
// data header frame.
|
|
112
|
-
dataOffset = packet.readUInt32BE(UpdatePacketHeader.PAYLOAD_SIZE) + UPDATE_PACKET_HEADER_SIZE;
|
|
113
|
-
// Validate our packet size, just in case we have more or less data than we expect. If we do, we're done for now.
|
|
114
|
-
if (packet.length !== (dataOffset + UPDATE_PACKET_HEADER_SIZE + packet.readUInt32BE(dataOffset + UpdatePacketHeader.PAYLOAD_SIZE))) {
|
|
115
|
-
throw new Error("Packet length doesn't match header information.");
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
catch (error) {
|
|
119
|
-
log.error("Realtime update API: error decoding update packet: %s.", error);
|
|
120
|
-
return null;
|
|
121
|
-
}
|
|
122
|
-
// Decode the action and payload frames now that we know where everything is.
|
|
123
|
-
const actionFrame = this.decodeUpdateFrame(log, packet.slice(0, dataOffset), UpdatePacketType.ACTION);
|
|
124
|
-
const payloadFrame = this.decodeUpdateFrame(log, packet.slice(dataOffset), UpdatePacketType.PAYLOAD);
|
|
125
|
-
if (!actionFrame || !payloadFrame) {
|
|
126
|
-
return null;
|
|
127
|
-
}
|
|
128
|
-
return ({ action: actionFrame, payload: payloadFrame });
|
|
129
|
-
}
|
|
130
|
-
// Decode a frame, composed of a header and payload, received through the update events API.
|
|
131
|
-
static decodeUpdateFrame(log, packet, packetType) {
|
|
132
|
-
// Read the packet frame type.
|
|
133
|
-
const frameType = packet.readUInt8(UpdatePacketHeader.TYPE);
|
|
134
|
-
// This isn't the frame type we were expecting - we're done.
|
|
135
|
-
if (packetType !== frameType) {
|
|
136
|
-
return null;
|
|
137
|
-
}
|
|
138
|
-
// Read the payload format.
|
|
139
|
-
const payloadFormat = packet.readUInt8(UpdatePacketHeader.PAYLOAD_FORMAT);
|
|
140
|
-
// Check to see if we're compressed or not, and inflate if needed after skipping past the 8-byte header.
|
|
141
|
-
const payload = packet.readUInt8(UpdatePacketHeader.DEFLATED) ? zlib_1.default.inflateSync(packet.slice(UPDATE_PACKET_HEADER_SIZE)) : packet.slice(UPDATE_PACKET_HEADER_SIZE);
|
|
142
|
-
// If it's an action, it can only have one format.
|
|
143
|
-
if (frameType === UpdatePacketType.ACTION) {
|
|
144
|
-
return (payloadFormat === UpdatePayloadType.JSON) ? JSON.parse(payload.toString()) : null;
|
|
145
|
-
}
|
|
146
|
-
// Process the payload format accordingly.
|
|
147
|
-
switch (payloadFormat) {
|
|
148
|
-
case UpdatePayloadType.JSON:
|
|
149
|
-
// If it's data payload, it can be anything.
|
|
150
|
-
return JSON.parse(payload.toString());
|
|
151
|
-
break;
|
|
152
|
-
case UpdatePayloadType.STRING:
|
|
153
|
-
return payload.toString("utf8");
|
|
154
|
-
break;
|
|
155
|
-
case UpdatePayloadType.BUFFER:
|
|
156
|
-
return payload;
|
|
157
|
-
break;
|
|
158
|
-
default:
|
|
159
|
-
log.error("Unknown payload packet type received in the realtime update events API: %s.", payloadFormat);
|
|
160
|
-
return null;
|
|
161
|
-
break;
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
exports.ProtectApiUpdates = ProtectApiUpdates;
|
|
166
|
-
//# sourceMappingURL=protect-api-updates.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"protect-api-updates.js","sourceRoot":"","sources":["../src/protect-api-updates.ts"],"names":[],"mappings":"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"}
|