homebridge-unifi-protect 6.22.0 → 7.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/README.md +29 -29
- package/config.schema.json +5 -15
- package/dist/devices/protect-camera-package.d.ts +0 -2
- package/dist/devices/protect-camera-package.js +4 -12
- package/dist/devices/protect-camera-package.js.map +1 -1
- package/dist/devices/protect-camera.d.ts +0 -3
- package/dist/devices/protect-camera.js +21 -119
- package/dist/devices/protect-camera.js.map +1 -1
- package/dist/devices/protect-chime.js +2 -6
- package/dist/devices/protect-chime.js.map +1 -1
- package/dist/devices/protect-device.d.ts +9 -5
- package/dist/devices/protect-device.js +17 -20
- package/dist/devices/protect-device.js.map +1 -1
- package/dist/devices/protect-doorbell.d.ts +0 -2
- package/dist/devices/protect-doorbell.js +39 -58
- package/dist/devices/protect-doorbell.js.map +1 -1
- package/dist/devices/protect-light.js +1 -3
- package/dist/devices/protect-light.js.map +1 -1
- package/dist/devices/protect-liveviews.js +2 -1
- package/dist/devices/protect-liveviews.js.map +1 -1
- package/dist/devices/protect-nvr-systeminfo.js +1 -1
- package/dist/devices/protect-nvr-systeminfo.js.map +1 -1
- package/dist/devices/protect-securitysystem.js +4 -4
- package/dist/devices/protect-securitysystem.js.map +1 -1
- package/dist/devices/protect-sensor.js +2 -6
- package/dist/devices/protect-sensor.js.map +1 -1
- package/dist/ffmpeg/protect-ffmpeg-codecs.js.map +1 -1
- package/dist/ffmpeg/protect-ffmpeg-exec.js.map +1 -1
- package/dist/ffmpeg/protect-ffmpeg-options.js +2 -20
- package/dist/ffmpeg/protect-ffmpeg-options.js.map +1 -1
- package/dist/ffmpeg/protect-ffmpeg-record.js +2 -1
- package/dist/ffmpeg/protect-ffmpeg-record.js.map +1 -1
- package/dist/ffmpeg/protect-ffmpeg-stream.js +14 -8
- package/dist/ffmpeg/protect-ffmpeg-stream.js.map +1 -1
- package/dist/ffmpeg/protect-ffmpeg.d.ts +2 -2
- package/dist/ffmpeg/protect-ffmpeg.js.map +1 -1
- package/dist/protect-events.d.ts +1 -1
- package/dist/protect-events.js +36 -33
- package/dist/protect-events.js.map +1 -1
- package/dist/protect-nvr.d.ts +7 -11
- package/dist/protect-nvr.js +14 -60
- package/dist/protect-nvr.js.map +1 -1
- package/dist/protect-options.d.ts +8 -23
- package/dist/protect-options.js +4 -128
- package/dist/protect-options.js.map +1 -1
- package/dist/protect-platform.d.ts +2 -4
- package/dist/protect-platform.js +3 -28
- package/dist/protect-platform.js.map +1 -1
- package/dist/protect-record.d.ts +2 -1
- package/dist/protect-record.js +21 -27
- package/dist/protect-record.js.map +1 -1
- package/dist/protect-snapshot.d.ts +2 -2
- package/dist/protect-snapshot.js +7 -3
- package/dist/protect-snapshot.js.map +1 -1
- package/dist/protect-stream.d.ts +3 -3
- package/dist/protect-stream.js +87 -90
- package/dist/protect-stream.js.map +1 -1
- package/dist/protect-timeshift.d.ts +9 -3
- package/dist/protect-timeshift.js +76 -24
- package/dist/protect-timeshift.js.map +1 -1
- package/dist/protect-types.d.ts +0 -7
- package/dist/protect-types.js +0 -1
- package/dist/protect-types.js.map +1 -1
- package/dist/settings.d.ts +2 -1
- package/dist/settings.js +4 -2
- package/dist/settings.js.map +1 -1
- package/homebridge-ui/public/index.html +42 -21
- package/homebridge-ui/public/lib/featureoptions.js +376 -0
- package/homebridge-ui/public/lib/featureoptions.js.map +1 -0
- package/homebridge-ui/public/lib/webUi-featureoptions.mjs +836 -0
- package/homebridge-ui/public/lib/webUi.mjs +184 -0
- package/homebridge-ui/public/ui.mjs +208 -124
- package/homebridge-ui/server.js +13 -39
- package/package.json +22 -21
- package/dist/protect-mqtt.d.ts +0 -20
- package/dist/protect-mqtt.js +0 -177
- package/dist/protect-mqtt.js.map +0 -1
- package/dist/protect-rtp.d.ts +0 -26
- package/dist/protect-rtp.js +0 -180
- package/dist/protect-rtp.js.map +0 -1
- package/homebridge-ui/public/lib/featureoptions.mjs +0 -201
- package/homebridge-ui/public/protect-featureoptions.mjs +0 -736
package/homebridge-ui/server.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
"use strict";
|
|
9
9
|
|
|
10
|
-
import { featureOptionCategories, featureOptions
|
|
10
|
+
import { featureOptionCategories, featureOptions } from "../dist/protect-options.js";
|
|
11
11
|
import { HomebridgePluginUiServer } from "@homebridge/plugin-ui-utils";
|
|
12
12
|
import { ProtectApi } from "unifi-protect";
|
|
13
13
|
import util from "node:util";
|
|
@@ -16,7 +16,8 @@ class PluginUiServer extends HomebridgePluginUiServer {
|
|
|
16
16
|
|
|
17
17
|
errorInfo;
|
|
18
18
|
|
|
19
|
-
constructor
|
|
19
|
+
constructor() {
|
|
20
|
+
|
|
20
21
|
super();
|
|
21
22
|
|
|
22
23
|
this.errorInfo = "";
|
|
@@ -44,6 +45,7 @@ class PluginUiServer extends HomebridgePluginUiServer {
|
|
|
44
45
|
return this.errorInfo;
|
|
45
46
|
} catch(err) {
|
|
46
47
|
|
|
48
|
+
// eslint-disable-next-line no-console
|
|
47
49
|
console.log(err);
|
|
48
50
|
|
|
49
51
|
// Return nothing if we error out for some reason.
|
|
@@ -62,22 +64,17 @@ class PluginUiServer extends HomebridgePluginUiServer {
|
|
|
62
64
|
|
|
63
65
|
const log = {
|
|
64
66
|
|
|
65
|
-
debug: (
|
|
67
|
+
debug: () => {},
|
|
66
68
|
error: (message, parameters = []) => {
|
|
67
69
|
|
|
68
70
|
// Save the error to inform the user in the webUI.
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
this.errorInfo = util.format(message, ...parameters);
|
|
72
|
-
} else {
|
|
73
|
-
|
|
74
|
-
this.errorInfo = util.format(message, parameters);
|
|
75
|
-
}
|
|
71
|
+
this.errorInfo = util.format(message, ...(Array.isArray(parameters) ? parameters : [parameters]));
|
|
76
72
|
|
|
73
|
+
// eslint-disable-next-line no-console
|
|
77
74
|
console.error(this.errorInfo);
|
|
78
75
|
},
|
|
79
|
-
info: (
|
|
80
|
-
warn: (
|
|
76
|
+
info: () => {},
|
|
77
|
+
warn: () => {}
|
|
81
78
|
};
|
|
82
79
|
|
|
83
80
|
// Connect to the Protect controller.
|
|
@@ -142,9 +139,11 @@ class PluginUiServer extends HomebridgePluginUiServer {
|
|
|
142
139
|
return aCase > bCase ? 1 : (bCase > aCase ? -1 : 0);
|
|
143
140
|
});
|
|
144
141
|
|
|
145
|
-
return [ ufpApi.bootstrap.nvr, ...ufpApi.bootstrap.cameras, ...ufpApi.bootstrap.chimes, ...ufpApi.bootstrap.lights, ...ufpApi.bootstrap.sensors,
|
|
142
|
+
return [ ufpApi.bootstrap.nvr, ...ufpApi.bootstrap.cameras, ...ufpApi.bootstrap.chimes, ...ufpApi.bootstrap.lights, ...ufpApi.bootstrap.sensors,
|
|
143
|
+
...ufpApi.bootstrap.viewers ];
|
|
146
144
|
} catch(err) {
|
|
147
145
|
|
|
146
|
+
// eslint-disable-next-line no-console
|
|
148
147
|
console.log(err);
|
|
149
148
|
|
|
150
149
|
// Return nothing if we error out for some reason.
|
|
@@ -157,32 +156,7 @@ class PluginUiServer extends HomebridgePluginUiServer {
|
|
|
157
156
|
#registerGetOptions() {
|
|
158
157
|
|
|
159
158
|
// Return the list of options configured for a given Protect device.
|
|
160
|
-
this.onRequest("/getOptions",
|
|
161
|
-
|
|
162
|
-
try {
|
|
163
|
-
|
|
164
|
-
const optionSet = {};
|
|
165
|
-
|
|
166
|
-
// Loop through all the feature option categories.
|
|
167
|
-
for(const category of featureOptionCategories) {
|
|
168
|
-
|
|
169
|
-
optionSet[category.name] = [];
|
|
170
|
-
|
|
171
|
-
for(const options of featureOptions[category.name]) {
|
|
172
|
-
|
|
173
|
-
options.value = isOptionEnabled(request.configOptions, request.nvrUfp, request.deviceUfp, category.name + "." + options.name, options.default);
|
|
174
|
-
optionSet[category.name].push(options);
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
return { categories: featureOptionCategories, options: optionSet };
|
|
179
|
-
|
|
180
|
-
} catch(err) {
|
|
181
|
-
|
|
182
|
-
// Return nothing if we error out for some reason.
|
|
183
|
-
return {};
|
|
184
|
-
}
|
|
185
|
-
});
|
|
159
|
+
this.onRequest("/getOptions", () => ({ categories: featureOptionCategories, options: featureOptions }));
|
|
186
160
|
}
|
|
187
161
|
}
|
|
188
162
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "homebridge-unifi-protect",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "7.0.0",
|
|
4
4
|
"displayName": "Homebridge UniFi Protect",
|
|
5
5
|
"description": "Homebridge UniFi Protect plugin providing complete HomeKit integration for the UniFi Protect ecosystem with full support for most features including autoconfiguration, motion detection, multiple controllers, and realtime updates.",
|
|
6
6
|
"author": {
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
},
|
|
19
19
|
"type": "module",
|
|
20
20
|
"engines": {
|
|
21
|
-
"homebridge": ">=1.
|
|
21
|
+
"homebridge": ">=1.8.0",
|
|
22
22
|
"node": ">=18"
|
|
23
23
|
},
|
|
24
24
|
"keywords": [
|
|
@@ -64,34 +64,35 @@
|
|
|
64
64
|
"rtsp"
|
|
65
65
|
],
|
|
66
66
|
"scripts": {
|
|
67
|
-
"
|
|
68
|
-
"
|
|
69
|
-
"
|
|
70
|
-
"
|
|
67
|
+
"prebuild": "npm run clean && npm run build-ui",
|
|
68
|
+
"build": "tsc",
|
|
69
|
+
"build-ui": "shx mkdir -p homebridge-ui/public/lib && shx cp \"node_modules/homebridge-plugin-utils/dist/ui/**/*.@(js|mjs){,.map}\" homebridge-ui/public/lib",
|
|
70
|
+
"clean": "shx rm -rf dist homebridge-ui/public/lib",
|
|
71
|
+
"prelint": "npm run build-ui",
|
|
72
|
+
"lint": "eslint eslint.config.mjs src/**.ts src/devices/**.ts src/ffmpeg/**.ts homebridge-ui/*.js homebridge-ui/public/**/*.mjs",
|
|
71
73
|
"postpublish": "npm run clean",
|
|
72
|
-
"prepublishOnly": "npm run lint && npm run build"
|
|
73
|
-
"test": "eslint src/**.ts src/devices/**.ts src/ffmpeg/**.ts",
|
|
74
|
-
"watch": "npm run build && npm link && nodemon"
|
|
74
|
+
"prepublishOnly": "npm run lint && npm run build"
|
|
75
75
|
},
|
|
76
76
|
"main": "dist/index.js",
|
|
77
77
|
"dependencies": {
|
|
78
78
|
"@homebridge/plugin-ui-utils": "1.0.3",
|
|
79
79
|
"ffmpeg-for-homebridge": "2.1.1",
|
|
80
|
-
"
|
|
81
|
-
"unifi-protect": "^4.
|
|
82
|
-
"ws": "8.
|
|
80
|
+
"homebridge-plugin-utils": "^1.3.0",
|
|
81
|
+
"unifi-protect": "^4.11.0",
|
|
82
|
+
"ws": "8.17.0"
|
|
83
83
|
},
|
|
84
84
|
"devDependencies": {
|
|
85
|
-
"@stylistic/eslint-plugin": "1.
|
|
86
|
-
"@types/node": "20.
|
|
87
|
-
"@types/readable-stream": "4.0.
|
|
85
|
+
"@stylistic/eslint-plugin": "2.1.0",
|
|
86
|
+
"@types/node": "20.14.0",
|
|
87
|
+
"@types/readable-stream": "4.0.14",
|
|
88
88
|
"@types/ws": "8.5.10",
|
|
89
|
-
"@typescript-eslint/eslint-plugin": "7.7.1",
|
|
90
|
-
"@typescript-eslint/parser": "7.7.1",
|
|
91
89
|
"eslint": "8.57.0",
|
|
92
|
-
"homebridge": "1.8.
|
|
93
|
-
"
|
|
94
|
-
"
|
|
95
|
-
"typescript": "
|
|
90
|
+
"homebridge": "1.8.2",
|
|
91
|
+
"shx": "^0.3.4",
|
|
92
|
+
"typescript": "5.4.5",
|
|
93
|
+
"typescript-eslint": "^7.12.0"
|
|
94
|
+
},
|
|
95
|
+
"optionalDependencies": {
|
|
96
|
+
"bufferutil": "^4.0.8"
|
|
96
97
|
}
|
|
97
98
|
}
|
package/dist/protect-mqtt.d.ts
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
/// <reference types="node" />
|
|
2
|
-
import { PlatformAccessory } from "homebridge";
|
|
3
|
-
import { ProtectNvr } from "./protect-nvr.js";
|
|
4
|
-
export declare class ProtectMqtt {
|
|
5
|
-
private config;
|
|
6
|
-
private isConnected;
|
|
7
|
-
private log;
|
|
8
|
-
private mqtt;
|
|
9
|
-
private nvr;
|
|
10
|
-
private subscriptions;
|
|
11
|
-
private ufpApi;
|
|
12
|
-
constructor(nvr: ProtectNvr);
|
|
13
|
-
private configure;
|
|
14
|
-
publish(accessory: PlatformAccessory | string, topic: string, message: string): void;
|
|
15
|
-
subscribe(accessory: PlatformAccessory | string, topic: string, callback: (cbBuffer: Buffer) => void): void;
|
|
16
|
-
subscribeGet(accessory: PlatformAccessory | string, topic: string, type: string, getValue: () => string): void;
|
|
17
|
-
subscribeSet(accessory: PlatformAccessory | string, topic: string, type: string, setValue: (value: string, rawValue: string) => Promise<void> | void): void;
|
|
18
|
-
unsubscribe(accessory: PlatformAccessory | string, topic: string): void;
|
|
19
|
-
private expandTopic;
|
|
20
|
-
}
|
package/dist/protect-mqtt.js
DELETED
|
@@ -1,177 +0,0 @@
|
|
|
1
|
-
/* Copyright(C) 2017-2024, HJD (https://github.com/hjdhjd). All rights reserved.
|
|
2
|
-
*
|
|
3
|
-
* protect-mqtt.ts: MQTT connectivity class for UniFi Protect.
|
|
4
|
-
*/
|
|
5
|
-
import mqtt from "mqtt";
|
|
6
|
-
import { PROTECT_MQTT_RECONNECT_INTERVAL } from "./settings.js";
|
|
7
|
-
export class ProtectMqtt {
|
|
8
|
-
config;
|
|
9
|
-
isConnected;
|
|
10
|
-
log;
|
|
11
|
-
mqtt;
|
|
12
|
-
nvr;
|
|
13
|
-
subscriptions;
|
|
14
|
-
ufpApi;
|
|
15
|
-
constructor(nvr) {
|
|
16
|
-
this.config = nvr.config;
|
|
17
|
-
this.isConnected = false;
|
|
18
|
-
this.log = nvr.log;
|
|
19
|
-
this.mqtt = null;
|
|
20
|
-
this.nvr = nvr;
|
|
21
|
-
this.subscriptions = {};
|
|
22
|
-
this.ufpApi = nvr.ufpApi;
|
|
23
|
-
if (!this.config.mqttUrl) {
|
|
24
|
-
return;
|
|
25
|
-
}
|
|
26
|
-
this.configure();
|
|
27
|
-
}
|
|
28
|
-
// Connect to the MQTT broker.
|
|
29
|
-
configure() {
|
|
30
|
-
// Try to connect to the MQTT broker and make sure we catch any URL errors.
|
|
31
|
-
try {
|
|
32
|
-
this.mqtt = mqtt.connect(this.config.mqttUrl, { reconnectPeriod: PROTECT_MQTT_RECONNECT_INTERVAL * 1000, rejectUnauthorized: false });
|
|
33
|
-
}
|
|
34
|
-
catch (error) {
|
|
35
|
-
if (error instanceof Error) {
|
|
36
|
-
switch (error.message) {
|
|
37
|
-
case "Missing protocol":
|
|
38
|
-
this.log.error("MQTT Broker: Invalid URL provided: %s.", this.config.mqttUrl);
|
|
39
|
-
break;
|
|
40
|
-
default:
|
|
41
|
-
this.log.error("MQTT Broker: Error: %s.", error.message);
|
|
42
|
-
break;
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
// We've been unable to even attempt to connect. It's likely we have a configuration issue - we're done here.
|
|
47
|
-
if (!this.mqtt) {
|
|
48
|
-
return;
|
|
49
|
-
}
|
|
50
|
-
// Notify the user when we connect to the broker.
|
|
51
|
-
this.mqtt.on("connect", () => {
|
|
52
|
-
this.isConnected = true;
|
|
53
|
-
// Magic incantation to redact passwords.
|
|
54
|
-
const redact = /^(?<pre>.*:\/{0,2}.*:)(?<pass>.*)(?<post>@.*)/;
|
|
55
|
-
this.log.info("Connected to MQTT broker: %s (topic: %s).", this.config.mqttUrl.replace(redact, "$<pre>REDACTED$<post>"), this.config.mqttTopic);
|
|
56
|
-
});
|
|
57
|
-
// Notify the user when we've disconnected.
|
|
58
|
-
this.mqtt.on("close", () => {
|
|
59
|
-
if (this.isConnected) {
|
|
60
|
-
this.isConnected = false;
|
|
61
|
-
// Magic incantation to redact passwords.
|
|
62
|
-
const redact = /^(?<pre>.*:\/{0,2}.*:)(?<pass>.*)(?<post>@.*)/;
|
|
63
|
-
this.log.info("Disconnected from MQTT broker: %s.", this.config.mqttUrl.replace(redact, "$<pre>REDACTED$<post>"));
|
|
64
|
-
}
|
|
65
|
-
});
|
|
66
|
-
// Process inbound messages and pass it to the right message handler.
|
|
67
|
-
this.mqtt.on("message", (topic, message) => {
|
|
68
|
-
if (this.subscriptions[topic]) {
|
|
69
|
-
this.subscriptions[topic](message);
|
|
70
|
-
}
|
|
71
|
-
});
|
|
72
|
-
// Notify the user when there's a connectivity error.
|
|
73
|
-
this.mqtt.on("error", (error) => {
|
|
74
|
-
switch (error.code) {
|
|
75
|
-
case "ECONNREFUSED":
|
|
76
|
-
this.log.error("MQTT Broker: Connection refused (url: %s). Will retry again in %s minute%s.", this.config.mqttUrl, PROTECT_MQTT_RECONNECT_INTERVAL / 60, PROTECT_MQTT_RECONNECT_INTERVAL / 60 > 1 ? "s" : "");
|
|
77
|
-
break;
|
|
78
|
-
case "ECONNRESET":
|
|
79
|
-
this.log.error("MQTT Broker: Connection reset (url: %s). Will retry again in %s minute%s.", this.config.mqttUrl, PROTECT_MQTT_RECONNECT_INTERVAL / 60, PROTECT_MQTT_RECONNECT_INTERVAL / 60 > 1 ? "s" : "");
|
|
80
|
-
break;
|
|
81
|
-
case "ENOTFOUND":
|
|
82
|
-
this.mqtt?.end(true);
|
|
83
|
-
this.log.error("MQTT Broker: Hostname or IP address not found. (url: %s).", this.config.mqttUrl);
|
|
84
|
-
break;
|
|
85
|
-
default:
|
|
86
|
-
this.log.error("MQTT Broker: %s (url: %s). Will retry again in %s minute%s.", error, this.config.mqttUrl, PROTECT_MQTT_RECONNECT_INTERVAL / 60, PROTECT_MQTT_RECONNECT_INTERVAL / 60 > 1 ? "s" : "");
|
|
87
|
-
break;
|
|
88
|
-
}
|
|
89
|
-
});
|
|
90
|
-
}
|
|
91
|
-
// Publish an MQTT event to a broker.
|
|
92
|
-
publish(accessory, topic, message) {
|
|
93
|
-
const expandedTopic = this.expandTopic(accessory, topic);
|
|
94
|
-
// No valid topic returned, we're done.
|
|
95
|
-
if (!expandedTopic) {
|
|
96
|
-
return;
|
|
97
|
-
}
|
|
98
|
-
this.log.debug("MQTT publish: %s Message: %s.", expandedTopic, message);
|
|
99
|
-
// By default, we publish as: unifi/protect/mac/event/name
|
|
100
|
-
this.mqtt?.publish(expandedTopic, message);
|
|
101
|
-
}
|
|
102
|
-
// Subscribe to an MQTT topic.
|
|
103
|
-
subscribe(accessory, topic, callback) {
|
|
104
|
-
const expandedTopic = this.expandTopic(accessory, topic);
|
|
105
|
-
// No valid topic returned, we're done.
|
|
106
|
-
if (!expandedTopic) {
|
|
107
|
-
return;
|
|
108
|
-
}
|
|
109
|
-
this.log.debug("MQTT subscribe: %s.", expandedTopic);
|
|
110
|
-
// Add to our callback list.
|
|
111
|
-
this.subscriptions[expandedTopic] = callback;
|
|
112
|
-
// Tell MQTT we're subscribing to this event.
|
|
113
|
-
// By default, we subscribe as: unifi/protect/mac/event/name.
|
|
114
|
-
this.mqtt?.subscribe(expandedTopic);
|
|
115
|
-
}
|
|
116
|
-
// Subscribe to a specific MQTT topic and publish a value on a get request.
|
|
117
|
-
subscribeGet(accessory, topic, type, getValue) {
|
|
118
|
-
// Return the current status of a given sensor.
|
|
119
|
-
this.subscribe(accessory, topic + "/get", (message) => {
|
|
120
|
-
const value = message.toString().toLowerCase();
|
|
121
|
-
// When we get the right message, we return the system information JSON.
|
|
122
|
-
if (value !== "true") {
|
|
123
|
-
return;
|
|
124
|
-
}
|
|
125
|
-
this.publish(accessory, topic, getValue());
|
|
126
|
-
((typeof accessory === "string") ? this.log : (this.nvr.configuredDevices[accessory.UUID]?.log ?? this.log)).info("MQTT: %s status published.", type);
|
|
127
|
-
});
|
|
128
|
-
}
|
|
129
|
-
// Subscribe to a specific MQTT topic and set a value on a set request.
|
|
130
|
-
subscribeSet(accessory, topic, type, setValue) {
|
|
131
|
-
// Return the current status of a given sensor.
|
|
132
|
-
this.subscribe(accessory, topic + "/set", (message) => {
|
|
133
|
-
const value = message.toString().toLowerCase();
|
|
134
|
-
const logResult = () => {
|
|
135
|
-
((typeof accessory === "string") ? this.log : (this.nvr.configuredDevices[accessory.UUID]?.log ?? this.log))
|
|
136
|
-
.info("MQTT: set message received for %s: %s.", type, value);
|
|
137
|
-
};
|
|
138
|
-
// Set our value and inform the user.
|
|
139
|
-
const result = setValue(value, message.toString());
|
|
140
|
-
if (result && typeof result.then === "function") {
|
|
141
|
-
result.then(() => {
|
|
142
|
-
logResult();
|
|
143
|
-
}).catch(error => {
|
|
144
|
-
((typeof accessory === "string") ? this.log : (this.nvr.configuredDevices[accessory.UUID]?.log ?? this.log))
|
|
145
|
-
.error("MQTT: error seting message received for %s: %s. %s", type, value, error);
|
|
146
|
-
});
|
|
147
|
-
return;
|
|
148
|
-
}
|
|
149
|
-
logResult();
|
|
150
|
-
});
|
|
151
|
-
}
|
|
152
|
-
// Unsubscribe to an MQTT topic.
|
|
153
|
-
unsubscribe(accessory, topic) {
|
|
154
|
-
const expandedTopic = this.expandTopic(accessory, topic);
|
|
155
|
-
// No valid topic returned, we're done.
|
|
156
|
-
if (!expandedTopic) {
|
|
157
|
-
return;
|
|
158
|
-
}
|
|
159
|
-
delete this.subscriptions[expandedTopic];
|
|
160
|
-
}
|
|
161
|
-
// Expand a topic to a unique, fully formed one.
|
|
162
|
-
expandTopic(accessory, topic) {
|
|
163
|
-
// No accessory, we're done.
|
|
164
|
-
if (!accessory) {
|
|
165
|
-
return null;
|
|
166
|
-
}
|
|
167
|
-
// Check if we were passed the MAC as an input. Otherwise, assume it's the controller's MAC initially.
|
|
168
|
-
let mac = (typeof accessory === "string") ? accessory : accessory.context.nvr;
|
|
169
|
-
// Check to see if it's really a Protect device...if it is, use it's MAC address.
|
|
170
|
-
if ((typeof accessory !== "string") && ("mac" in accessory.context)) {
|
|
171
|
-
mac = accessory.context.mac;
|
|
172
|
-
}
|
|
173
|
-
// Return our fully expanded topic string.
|
|
174
|
-
return this.config.mqttTopic + "/" + mac + "/" + topic;
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
//# sourceMappingURL=protect-mqtt.js.map
|
package/dist/protect-mqtt.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"protect-mqtt.js","sourceRoot":"","sources":["../src/protect-mqtt.ts"],"names":[],"mappings":"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"}
|
package/dist/protect-rtp.d.ts
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
/// <reference types="node" />
|
|
2
|
-
/// <reference types="node" />
|
|
3
|
-
import { EventEmitter } from "node:events";
|
|
4
|
-
import { ProtectStreamingDelegate } from "./protect-stream.js";
|
|
5
|
-
export declare class RtpDemuxer extends EventEmitter {
|
|
6
|
-
private delegate;
|
|
7
|
-
private heartbeatTimer;
|
|
8
|
-
private heartbeatMsg;
|
|
9
|
-
private _isRunning;
|
|
10
|
-
private log;
|
|
11
|
-
private inputPort;
|
|
12
|
-
readonly socket: import("dgram").Socket;
|
|
13
|
-
constructor(streamingDelegate: ProtectStreamingDelegate, ipFamily: ("ipv4" | "ipv6"), inputPort: number, rtcpPort: number, rtpPort: number);
|
|
14
|
-
private heartbeat;
|
|
15
|
-
close(): void;
|
|
16
|
-
private getPayloadType;
|
|
17
|
-
private isRtpMessage;
|
|
18
|
-
get isRunning(): boolean;
|
|
19
|
-
}
|
|
20
|
-
export declare class RtpPortAllocator {
|
|
21
|
-
private portsInUse;
|
|
22
|
-
constructor();
|
|
23
|
-
private getPort;
|
|
24
|
-
reserve(ipFamily?: ("ipv4" | "ipv6"), portCount?: (1 | 2), attempts?: number): Promise<number>;
|
|
25
|
-
cancel(port: number): void;
|
|
26
|
-
}
|
package/dist/protect-rtp.js
DELETED
|
@@ -1,180 +0,0 @@
|
|
|
1
|
-
/* Copyright(C) 2017-2024, HJD (https://github.com/hjdhjd). All rights reserved.
|
|
2
|
-
*
|
|
3
|
-
* protect-rtp.ts: RTP-related utilities to slice and dice RTP streams.
|
|
4
|
-
*
|
|
5
|
-
* This module is heavily inspired by the homebridge and homebridge-camera-ffmpeg source code and
|
|
6
|
-
* borrows heavily from both. Thank you for your contributions to the HomeKit world.
|
|
7
|
-
*/
|
|
8
|
-
import { EventEmitter, once } from "node:events";
|
|
9
|
-
import { PROTECT_TWOWAY_HEARTBEAT_INTERVAL } from "./settings.js";
|
|
10
|
-
import { createSocket } from "node:dgram";
|
|
11
|
-
/*
|
|
12
|
-
* Here's the problem this class solves: FFmpeg doesn't support multiplexing RTP and RTCP data on a single UDP port (RFC 5761). If it did, we wouldn't need this
|
|
13
|
-
* workaround for HomeKit compatibility, which does multiplex RTP and RTCP over a single UDP port.
|
|
14
|
-
*
|
|
15
|
-
* This class inspects all packets coming in from inputPort and demultiplexes RTP and RTCP traffic to rtpPort and rtcpPort, respectively.
|
|
16
|
-
*
|
|
17
|
-
* Credit to @dgreif and @brandawg93 who graciously shared their code as a starting point, and their collaboration in answering the questions needed to bring all this
|
|
18
|
-
* together. A special thank you to @Sunoo for the many hours of discussion and brainstorming on this and other topics.
|
|
19
|
-
*/
|
|
20
|
-
export class RtpDemuxer extends EventEmitter {
|
|
21
|
-
delegate;
|
|
22
|
-
heartbeatTimer;
|
|
23
|
-
heartbeatMsg;
|
|
24
|
-
_isRunning;
|
|
25
|
-
log;
|
|
26
|
-
inputPort;
|
|
27
|
-
socket;
|
|
28
|
-
// Create an instance of RtpDemuxer.
|
|
29
|
-
constructor(streamingDelegate, ipFamily, inputPort, rtcpPort, rtpPort) {
|
|
30
|
-
super();
|
|
31
|
-
this._isRunning = false;
|
|
32
|
-
this.delegate = streamingDelegate;
|
|
33
|
-
this.log = streamingDelegate.log;
|
|
34
|
-
this.inputPort = inputPort;
|
|
35
|
-
this.socket = createSocket(ipFamily === "ipv6" ? "udp6" : "udp4");
|
|
36
|
-
// Catch errors when they happen on our demuxer.
|
|
37
|
-
this.socket.on("error", (error) => {
|
|
38
|
-
this.log.error("RtpDemuxer Error: %s", error);
|
|
39
|
-
this.socket.close();
|
|
40
|
-
});
|
|
41
|
-
// Split the message into RTP and RTCP packets.
|
|
42
|
-
this.socket.on("message", (msg) => {
|
|
43
|
-
// Send RTP packets to the RTP port.
|
|
44
|
-
if (this.isRtpMessage(msg)) {
|
|
45
|
-
this.emit("rtp");
|
|
46
|
-
this.socket.send(msg, rtpPort);
|
|
47
|
-
}
|
|
48
|
-
else {
|
|
49
|
-
// Save this RTCP message for heartbeat purposes for the RTP port. This works because RTCP packets will be ignored
|
|
50
|
-
// by ffmpeg on the RTP port, effectively providing a heartbeat to ensure FFmpeg doesn't timeout if there's an
|
|
51
|
-
// extended delay between data transmission.
|
|
52
|
-
this.heartbeatMsg = Buffer.from(msg);
|
|
53
|
-
// Clear the old heartbeat timer.
|
|
54
|
-
clearTimeout(this.heartbeatTimer);
|
|
55
|
-
this.heartbeat(rtpPort);
|
|
56
|
-
// RTCP control packets should go to the RTCP port.
|
|
57
|
-
this.socket.send(msg, rtcpPort);
|
|
58
|
-
}
|
|
59
|
-
});
|
|
60
|
-
this.log.debug("Creating an RtpDemuxer instance - inbound port: %s, RTCP port: %s, RTP port: %s.", this.inputPort, rtcpPort, rtpPort);
|
|
61
|
-
// Take the socket live.
|
|
62
|
-
this.socket.bind(this.inputPort);
|
|
63
|
-
this._isRunning = true;
|
|
64
|
-
}
|
|
65
|
-
// Send a regular heartbeat to FFmpeg to ensure the pipe remains open and the process alive.
|
|
66
|
-
heartbeat(port) {
|
|
67
|
-
// Clear the old heartbeat timer.
|
|
68
|
-
clearTimeout(this.heartbeatTimer);
|
|
69
|
-
// Send a heartbeat to FFmpeg every few seconds to keep things open. FFmpeg has a five-second timeout
|
|
70
|
-
// in reading input, and we want to be comfortably within the margin for error to ensure the process
|
|
71
|
-
// continues to run.
|
|
72
|
-
this.heartbeatTimer = setTimeout(() => {
|
|
73
|
-
this.log.debug("Sending ffmpeg a heartbeat.");
|
|
74
|
-
this.socket.send(this.heartbeatMsg, port);
|
|
75
|
-
this.heartbeat(port);
|
|
76
|
-
}, PROTECT_TWOWAY_HEARTBEAT_INTERVAL * 1000);
|
|
77
|
-
}
|
|
78
|
-
// Close the socket and cleanup.
|
|
79
|
-
close() {
|
|
80
|
-
this.log.debug("Closing the RtpDemuxer instance on port %s.", this.inputPort);
|
|
81
|
-
clearTimeout(this.heartbeatTimer);
|
|
82
|
-
this.socket.close();
|
|
83
|
-
this._isRunning = false;
|
|
84
|
-
this.emit("rtp");
|
|
85
|
-
}
|
|
86
|
-
// Retrieve the payload information from a packet to discern what the packet payload is.
|
|
87
|
-
getPayloadType(message) {
|
|
88
|
-
return message.readUInt8(1) & 0x7f;
|
|
89
|
-
}
|
|
90
|
-
// Return whether or not a packet is RTP (or not).
|
|
91
|
-
isRtpMessage(message) {
|
|
92
|
-
const payloadType = this.getPayloadType(message);
|
|
93
|
-
return (payloadType > 90) || (payloadType === 0);
|
|
94
|
-
}
|
|
95
|
-
// Inform people whether we are up and running or not.
|
|
96
|
-
get isRunning() {
|
|
97
|
-
return this._isRunning;
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
/* RTP port allocator class that keeps track of UDP ports that are currently earmarked for use. We need this when allocating ports that we use for various network
|
|
101
|
-
* activities such as demuxing FFmpeg or opening up other sockets. Otherwise, we run a risk (especially in environment where there are many such requests) of allocating
|
|
102
|
-
* the same port multiple times and end up erroring out unceremoniously.
|
|
103
|
-
*/
|
|
104
|
-
export class RtpPortAllocator {
|
|
105
|
-
portsInUse;
|
|
106
|
-
// Instantiate our port retrieval.
|
|
107
|
-
constructor() {
|
|
108
|
-
// Initialize our in use tracker.
|
|
109
|
-
this.portsInUse = {};
|
|
110
|
-
}
|
|
111
|
-
// Find an available UDP port by binding to one to validate it's availability.
|
|
112
|
-
async getPort(ipFamily, port = 0) {
|
|
113
|
-
try {
|
|
114
|
-
// Keep looping until we find what we're looking for: local UDP ports that are unspoken for.
|
|
115
|
-
for (;;) {
|
|
116
|
-
// Create a datagram socket, so we can use it to find a port.
|
|
117
|
-
const socket = createSocket(ipFamily === "ipv6" ? "udp6" : "udp4");
|
|
118
|
-
// Exclude this socket from Node's reference counting so we don't have issues later.
|
|
119
|
-
socket.unref();
|
|
120
|
-
// Listen for the bind event.
|
|
121
|
-
const eventListener = once(socket, "listening");
|
|
122
|
-
// Bind to the port in question. If port is set to 0, we'll get a randomly generated port generated for us.
|
|
123
|
-
socket.bind(port);
|
|
124
|
-
// Ensure we wait for the socket to be bound.
|
|
125
|
-
// eslint-disable-next-line no-await-in-loop
|
|
126
|
-
await eventListener;
|
|
127
|
-
// Retrieve the port number we've gotten from the bind request.
|
|
128
|
-
const assignedPort = socket.address().port;
|
|
129
|
-
// We're done with the socket, let's cleanup.
|
|
130
|
-
socket.close();
|
|
131
|
-
// Check to see if the port is one we're already using. If it is, try again.
|
|
132
|
-
if (this.portsInUse[assignedPort]) {
|
|
133
|
-
continue;
|
|
134
|
-
}
|
|
135
|
-
// Now let's mark the port in use.
|
|
136
|
-
this.portsInUse[assignedPort] = true;
|
|
137
|
-
// Return the port.
|
|
138
|
-
return assignedPort;
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
catch (error) {
|
|
142
|
-
return -1;
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
// Reserve consecutive ports for use with FFmpeg. FFmpeg currently lacks the ability to specify both the RTP and RTCP ports. FFmpeg always assumes, by convention, that
|
|
146
|
-
// when you specify an RTP port, the RTCP port is the RTP port + 1. In order to work around that challenge, we need to always ensure that when we reserve multiple ports
|
|
147
|
-
// for RTP (primarily for two-way audio) that we we are reserving consecutive ports only.
|
|
148
|
-
async reserve(ipFamily = "ipv4", portCount = 1, attempts = 0) {
|
|
149
|
-
// Sanity check and make sure we're not requesting any more than two ports at a time, or if we've exceeded our attempt limit.
|
|
150
|
-
if (![1, 2].includes(portCount) || (attempts > 10)) {
|
|
151
|
-
return -1;
|
|
152
|
-
}
|
|
153
|
-
let firstPort = 0;
|
|
154
|
-
// Find the appropriate number of ports being requested.
|
|
155
|
-
for (let i = 0; i < portCount; i++) {
|
|
156
|
-
// eslint-disable-next-line no-await-in-loop
|
|
157
|
-
const assignedPort = await this.getPort(ipFamily, firstPort ? firstPort + 1 : 0);
|
|
158
|
-
// We haven't gotten a port, let's try again.
|
|
159
|
-
if (assignedPort === -1) {
|
|
160
|
-
// If we've gotten the first port of a pair of ports, make sure we release it here.
|
|
161
|
-
if (firstPort) {
|
|
162
|
-
this.cancel(firstPort);
|
|
163
|
-
}
|
|
164
|
-
// We still haven't found what we're looking for...keep looking.
|
|
165
|
-
return this.reserve(ipFamily, portCount, attempts++);
|
|
166
|
-
}
|
|
167
|
-
// We've seen the first port we may be looking for, let's save it.
|
|
168
|
-
if (!firstPort) {
|
|
169
|
-
firstPort = assignedPort;
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
// Return the first port we've found.
|
|
173
|
-
return firstPort;
|
|
174
|
-
}
|
|
175
|
-
// Delete a port reservation that's no longer needed.
|
|
176
|
-
cancel(port) {
|
|
177
|
-
delete this.portsInUse[port];
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
//# sourceMappingURL=protect-rtp.js.map
|
package/dist/protect-rtp.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"protect-rtp.js","sourceRoot":"","sources":["../src/protect-rtp.ts"],"names":[],"mappings":"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"}
|