homebridge-unifi-protect 6.22.0 → 7.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 +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 +2 -3
- package/dist/devices/protect-camera.js +35 -124
- 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 +10 -6
- package/dist/devices/protect-device.js +23 -22
- package/dist/devices/protect-device.js.map +1 -1
- package/dist/devices/protect-doorbell.d.ts +0 -2
- package/dist/devices/protect-doorbell.js +41 -60
- 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/devices/protect-viewer.js +1 -1
- package/dist/devices/protect-viewer.js.map +1 -1
- package/dist/ffmpeg/protect-ffmpeg-codecs.js.map +1 -1
- package/dist/ffmpeg/protect-ffmpeg-exec.js +1 -1
- package/dist/ffmpeg/protect-ffmpeg-exec.js.map +1 -1
- package/dist/ffmpeg/protect-ffmpeg-options.js +2 -19
- 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 -3
- package/dist/protect-events.js +38 -43
- package/dist/protect-events.js.map +1 -1
- package/dist/protect-livestream.d.ts +12 -0
- package/dist/protect-livestream.js +63 -0
- package/dist/protect-livestream.js.map +1 -0
- package/dist/protect-nvr.d.ts +7 -11
- package/dist/protect-nvr.js +15 -61
- 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 +4 -3
- package/dist/protect-record.js +23 -29
- 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 -4
- package/dist/protect-stream.js +116 -98
- package/dist/protect-stream.js.map +1 -1
- package/dist/protect-timeshift.d.ts +16 -10
- package/dist/protect-timeshift.js +88 -51
- 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 +213 -124
- package/homebridge-ui/server.js +13 -52
- 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/README.md
CHANGED
|
@@ -75,35 +75,35 @@ I hope to continue to work on this one to get AEC working for two-way audio. [Yo
|
|
|
75
75
|
* [Plugin Configuration Reference](https://github.com/hjdhjd/homebridge-unifi-protect/blob/main/docs/ConfigurationReference.md): complete list of configuration options available in this plugin.
|
|
76
76
|
|
|
77
77
|
## Installation
|
|
78
|
-
### Prerequisites:
|
|
79
78
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
- Only official
|
|
105
|
-
-
|
|
106
|
-
-
|
|
79
|
+
> [!IMPORTANT]
|
|
80
|
+
> ### Prerequisites
|
|
81
|
+
> * Ensure you are using a machine that can handle the CPU and GPU requirements of `homebridge-unifi-protect`. The more cameras you have, the higher the performance requirements. If you intend to use HomeKit Secure Video, in particular, you will need a capable, modern CPU. Raspberry Pi 4 is a great piece of hardware, but it cannot keep up with the demands of more than a few Protect cameras, and definitely not the higher end members of the Protect camera ecosystem.
|
|
82
|
+
> * If you are new to Homebridge, please first read the [Homebridge](https://homebridge.io) [documentation](https://github.com/homebridge/homebridge/wiki) and installation instructions before proceeding. Ensure you've installed Homebridge and the [Homebridge Config UI](https://github.com/homebridge/homebridge-config-ui-x) before proceeding.
|
|
83
|
+
> * Ensure you have a ***local user*** account on your UniFi console dedicated to `homebridge-unifi-protect`. To create a local user account on your UniFi console:
|
|
84
|
+
> * Go to the ***OS Settings*** tab on the Protect controller web interface. This is typicaly near the top left of the Protect controller UI page.
|
|
85
|
+
> * Click ***Add Admin*** located near the top right of the ***OS Settings*** page.
|
|
86
|
+
> * Click ***Restrict to local access only*** and then enter in a username and password for the new local user.
|
|
87
|
+
> * Optionally customize the role of the user to adjust the roles. HBUP requires the ***Full Management*** role for all of it's capabilities to work, although it will work in a more limited form without administrative privileges.
|
|
88
|
+
>
|
|
89
|
+
> ### Getting Started
|
|
90
|
+
> To install `homebridge-unifi-protect`:
|
|
91
|
+
>
|
|
92
|
+
> 1. Go to the `Plugins` tab in the Homebridge Config UI and searching for `homebridge-unifi-protect` and install it.
|
|
93
|
+
> 2. Click on the ***Set Up*** icon located in the top right corner of the Homebridge UniFi Protect tile and then enter the hostname or IP address of the Protect controller as well as the username and password you created in the steps above and then login to the Protect controller.
|
|
94
|
+
> 3. For the moment, don't make any other configuration changes and click ***Save*** and then click ***Restart Homebridge***.
|
|
95
|
+
> 4. After restarting Homebridge, click on the ***Set Up Child Bridge*** icon located in the top right corner of the Homebridge UniFi Protect tile. Toggle the child bridge setting for UniFi Protect to ***on*** and then save and restart Homebridge.
|
|
96
|
+
> 5. After restarting Homebridge, click on the ***Connect to HomeKit*** icon located in the top right corner of the Homebridge UniFi Protect tile. Use the Home app on your iPhone and scan the QR code to connect UniFi Protect to HomeKit. The Home app may ask questions about where to locate your cameras and whether you want to enable HomeKit Secure Video. Answer according to your preferences.
|
|
97
|
+
> 6. That's it! You should now be able to access all your UniFi Protect devices in HomeKit. You can further tailor your experience by going to the Homebridge UniFi Protect webUI and exploring the various features and options that are available to you.
|
|
98
|
+
|
|
99
|
+
> [!NOTE]
|
|
100
|
+
> HBUP includes everything required to get up and running on many of the [more popular platforms and operating systems](https://github.com/homebridge/ffmpeg-for-homebridge#supported-platforms). **If you're running on an unsupported platform, you will need to install a working version of *FFmpeg* for `homebridge-unifi-protect` to work correctly with your cameras. Additionally, your FFmpeg will need to support the *fdk-aac* codec if you want audio support to work. Setting up and configuring FFmpeg is beyond the scope of this documentation.**
|
|
101
|
+
|
|
102
|
+
> [!TIP]
|
|
103
|
+
> - Only official releases of UniFi Protect and UniFi OS firmwares are supported. **No beta, early access, or release candidate versions of any kind are supported**.
|
|
104
|
+
> - Only official hardware releases for UniFi Protect are supported. Early access or beta hardware is unsupported.
|
|
105
|
+
> - No support is provided for any beta versions of Apple operating systems (iOS, iPadOS, macOS, tvOS, etc.).
|
|
106
|
+
> - **My philosophy is to aggressively adopt the capability and features (that make sense in a HomeKit context) in the latest official Ubiquiti firmware releases and to deprecate old functionality that's been superceded by newer, richer, or more performant capabilities - either by HomeKit or Ubiquiti. Read the [Changelog](https://github.com/hjdhjd/homebridge-unifi-protect/blob/main/docs/Changelog.md) carefully for the latest information on what's new.**
|
|
107
107
|
|
|
108
108
|
## Plugin Development Dashboard
|
|
109
109
|
This is mostly of interest to the true developer nerds amongst us.
|
|
@@ -111,4 +111,4 @@ This is mostly of interest to the true developer nerds amongst us.
|
|
|
111
111
|
[](https://github.com/hjdhjd/homebridge-unifi-protect/blob/main/LICENSE.md)
|
|
112
112
|
[](https://github.com/hjdhjd/homebridge-unifi-protect/actions?query=workflow%3A%22Continuous+Integration%22)
|
|
113
113
|
[](https://libraries.io/npm/homebridge-unifi-protect)
|
|
114
|
-
[](https://github.com/hjdhjd/homebridge-unifi-protect/commits/
|
|
114
|
+
[](https://github.com/hjdhjd/homebridge-unifi-protect/commits/main)
|
package/config.schema.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"pluginAlias": "UniFi Protect",
|
|
3
3
|
"pluginType": "platform",
|
|
4
4
|
"headerDisplay": "Full HomeKit support UniFi Protect devices. See the [homebridge-unifi-protect](https://github.com/hjdhjd/homebridge-unifi-protect) developer page for detailed documentation.",
|
|
5
|
-
"footerDisplay": "A working **
|
|
5
|
+
"footerDisplay": "A working **FFmpeg** installation is required for this plugin to work. For audio support, make sure your version of ffmpeg is compiled with support for the **fdk-aac** codec.",
|
|
6
6
|
"singular": true,
|
|
7
7
|
"customUi": true,
|
|
8
8
|
|
|
@@ -28,15 +28,6 @@
|
|
|
28
28
|
"description": "Hostname or IP address of your UniFi Protect controller."
|
|
29
29
|
},
|
|
30
30
|
|
|
31
|
-
"defaultDoorbellMessage": {
|
|
32
|
-
"type": "string",
|
|
33
|
-
"title": "Default doorbell message",
|
|
34
|
-
"required": false,
|
|
35
|
-
"maxLength": 30,
|
|
36
|
-
"placeholder": "e.g. WELCOME",
|
|
37
|
-
"description": "Default message to be used on UniFi Protect doorbells. Default: WELCOME."
|
|
38
|
-
},
|
|
39
|
-
|
|
40
31
|
"doorbellMessages": {
|
|
41
32
|
"type": "array",
|
|
42
33
|
"title": "Doorbell Messages",
|
|
@@ -138,8 +129,8 @@
|
|
|
138
129
|
"type": "string",
|
|
139
130
|
"title": "Feature Option",
|
|
140
131
|
"required": false,
|
|
141
|
-
"description": "Enter only one option per entry.
|
|
142
|
-
"placeholder": "e.g. Disable.
|
|
132
|
+
"description": "Enter only one option per entry. Use the feature options webUI tab above unless you know what you're doing.",
|
|
133
|
+
"placeholder": "e.g. Disable.Device"
|
|
143
134
|
}
|
|
144
135
|
},
|
|
145
136
|
|
|
@@ -210,7 +201,6 @@
|
|
|
210
201
|
"expanded": false,
|
|
211
202
|
"items": [
|
|
212
203
|
|
|
213
|
-
"controllers[].defaultDoorbellMessage",
|
|
214
204
|
{
|
|
215
205
|
"key": "controllers[].doorbellMessages",
|
|
216
206
|
"type": "array",
|
|
@@ -273,8 +263,8 @@
|
|
|
273
263
|
"key": "options",
|
|
274
264
|
"type": "array",
|
|
275
265
|
"name": " ",
|
|
276
|
-
"description": "
|
|
277
|
-
"orderable":
|
|
266
|
+
"description": "Use the feature options webUI tab above instead of manually configuring feature options here.",
|
|
267
|
+
"orderable": true,
|
|
278
268
|
"buttonText": "Add Feature Option",
|
|
279
269
|
"items": [
|
|
280
270
|
"options[]"
|
|
@@ -13,10 +13,12 @@ export class ProtectCameraPackage extends ProtectCamera {
|
|
|
13
13
|
this.hasHksv = true;
|
|
14
14
|
this.hints.probesize = 32768;
|
|
15
15
|
if (parentCamera) {
|
|
16
|
-
this.hints.
|
|
16
|
+
this.hints.apiStreaming = parentCamera.hints.apiStreaming;
|
|
17
17
|
this.hints.hardwareDecoding = parentCamera.hints.hardwareDecoding;
|
|
18
|
-
this.hints.hardwareTranscoding =
|
|
18
|
+
this.hints.hardwareTranscoding = parentCamera.hints.hardwareTranscoding;
|
|
19
|
+
this.hints.highResSnapshots = parentCamera.hints.highResSnapshots;
|
|
19
20
|
this.hints.logHksv = parentCamera.hints.logHksv;
|
|
21
|
+
this.hints.timeshift = parentCamera.hints.timeshift;
|
|
20
22
|
}
|
|
21
23
|
// Clean out the context object in case it's been polluted somehow.
|
|
22
24
|
this.accessory.context = {};
|
|
@@ -102,15 +104,5 @@ export class ProtectCameraPackage extends ProtectCamera {
|
|
|
102
104
|
findRecordingRtsp() {
|
|
103
105
|
return this.findRtsp();
|
|
104
106
|
}
|
|
105
|
-
// Get the current bitrate for a specific camera channel.
|
|
106
|
-
getBitrate(channelId) {
|
|
107
|
-
// Find the right channel.
|
|
108
|
-
const channel = this.ufp.channels.find(x => x.id === channelId);
|
|
109
|
-
return channel?.bitrate ?? -1;
|
|
110
|
-
}
|
|
111
|
-
// Set the bitrate for a specific camera channel.
|
|
112
|
-
setBitrate() {
|
|
113
|
-
return Promise.resolve(true);
|
|
114
|
-
}
|
|
115
107
|
}
|
|
116
108
|
//# sourceMappingURL=protect-camera-package.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"protect-camera-package.js","sourceRoot":"","sources":["../../src/devices/protect-camera-package.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,aAAa,EAAa,MAAM,qBAAqB,CAAC;AAC/D,OAAO,EAAE,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AAGhE,gKAAgK;AAChK,MAAM,OAAO,oBAAqB,SAAQ,aAAa;IAErD,gCAAgC;IACtB,KAAK,CAAC,eAAe;QAE7B,yBAAyB;QACzB,MAAM,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAExD,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;QACpB,IAAI,CAAC,KAAK,CAAC,SAAS,GAAG,KAAK,CAAC;QAE7B,IAAG,YAAY,EAAE,CAAC;YAEhB,IAAI,CAAC,KAAK,CAAC,
|
|
1
|
+
{"version":3,"file":"protect-camera-package.js","sourceRoot":"","sources":["../../src/devices/protect-camera-package.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { PlatformAccessory, Resolution } from "homebridge";
|
|
2
2
|
import { ProtectCameraChannelConfig, ProtectCameraConfig, ProtectEventPacket } from "unifi-protect";
|
|
3
|
+
import { LivestreamManager } from "../protect-livestream.js";
|
|
3
4
|
import { ProtectDevice } from "./protect-device.js";
|
|
4
5
|
import { ProtectNvr } from "../protect-nvr.js";
|
|
5
6
|
import { ProtectStreamingDelegate } from "../protect-stream.js";
|
|
@@ -21,6 +22,7 @@ export declare class ProtectCamera extends ProtectDevice {
|
|
|
21
22
|
private isDeleted;
|
|
22
23
|
isRinging: boolean;
|
|
23
24
|
detectLicensePlate: string[];
|
|
25
|
+
readonly livestream: LivestreamManager;
|
|
24
26
|
private rtspEntries;
|
|
25
27
|
private rtspQuality;
|
|
26
28
|
stream: ProtectStreamingDelegate;
|
|
@@ -39,12 +41,9 @@ export declare class ProtectCamera extends ProtectDevice {
|
|
|
39
41
|
private configureVideoStream;
|
|
40
42
|
private configureHksv;
|
|
41
43
|
private configureHksvRecordingSwitch;
|
|
42
|
-
private configureDynamicBitrateSwitch;
|
|
43
44
|
private configureNvrRecordingSwitch;
|
|
44
45
|
protected configureMqtt(): boolean;
|
|
45
46
|
updateDevice(): boolean;
|
|
46
|
-
getBitrate(channelId: number): number;
|
|
47
|
-
setBitrate(channelId: number, value: number): Promise<boolean>;
|
|
48
47
|
private findRtspEntry;
|
|
49
48
|
findRtsp(width: number, height: number, options?: RtspOptions): RtspEntry | null;
|
|
50
49
|
findRecordingRtsp(width: number, height: number): RtspEntry | null;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { ProtectReservedNames, toCamelCase } from "../protect-types.js";
|
|
2
|
-
import {
|
|
2
|
+
import { LivestreamManager } from "../protect-livestream.js";
|
|
3
|
+
import { PROTECT_HKSV_IDR_INTERVAL } from "../settings.js";
|
|
3
4
|
import { ProtectDevice } from "./protect-device.js";
|
|
4
5
|
import { ProtectStreamingDelegate } from "../protect-stream.js";
|
|
5
6
|
export class ProtectCamera extends ProtectDevice {
|
|
@@ -7,6 +8,7 @@ export class ProtectCamera extends ProtectDevice {
|
|
|
7
8
|
isDeleted;
|
|
8
9
|
isRinging;
|
|
9
10
|
detectLicensePlate;
|
|
11
|
+
livestream;
|
|
10
12
|
rtspEntries;
|
|
11
13
|
rtspQuality;
|
|
12
14
|
stream;
|
|
@@ -18,6 +20,7 @@ export class ProtectCamera extends ProtectDevice {
|
|
|
18
20
|
this.isDeleted = false;
|
|
19
21
|
this.isRinging = false;
|
|
20
22
|
this.detectLicensePlate = [];
|
|
23
|
+
this.livestream = new LivestreamManager(this);
|
|
21
24
|
this.rtspEntries = [];
|
|
22
25
|
this.rtspQuality = {};
|
|
23
26
|
this.ufp = device;
|
|
@@ -28,6 +31,7 @@ export class ProtectCamera extends ProtectDevice {
|
|
|
28
31
|
configureHints() {
|
|
29
32
|
// Configure our parent's hints.
|
|
30
33
|
super.configureHints();
|
|
34
|
+
this.hints.apiStreaming = this.hasFeature("Video.Stream.UseApi");
|
|
31
35
|
this.hints.crop = this.hasFeature("Video.Crop");
|
|
32
36
|
this.hints.hardwareDecoding = true;
|
|
33
37
|
this.hints.hardwareTranscoding = this.hasFeature("Video.Transcode.Hardware");
|
|
@@ -59,17 +63,12 @@ export class ProtectCamera extends ProtectDevice {
|
|
|
59
63
|
// Clean out the context object in case it's been polluted somehow.
|
|
60
64
|
this.accessory.context = {};
|
|
61
65
|
this.accessory.context.detectMotion = savedContext.detectMotion ?? true;
|
|
62
|
-
this.accessory.context.dynamicBitrate = savedContext.dynamicBitrate ?? false;
|
|
63
66
|
this.accessory.context.hksvRecording = savedContext.hksvRecording ?? true;
|
|
64
67
|
this.accessory.context.mac = this.ufp.mac;
|
|
65
68
|
this.accessory.context.nvr = this.nvr.ufp.mac;
|
|
66
69
|
// Inform the user that motion detection will suck.
|
|
67
70
|
if (this.ufp.recordingSettings.mode === "never") {
|
|
68
|
-
this.log.
|
|
69
|
-
}
|
|
70
|
-
// Inform the user if we have enabled the dynamic bitrate setting.
|
|
71
|
-
if (this.hasFeature("Video.DynamicBitrate")) {
|
|
72
|
-
this.log.info("Dynamic streaming bitrate adjustment on the UniFi Protect controller enabled.");
|
|
71
|
+
this.log.warn("Motion events will not be generated by the Protect controller when the controller's camera recording options are set to \"never\".");
|
|
73
72
|
}
|
|
74
73
|
// Check to see if we have smart motion events enabled on a supported camera.
|
|
75
74
|
if (this.hints.smartDetect) {
|
|
@@ -81,6 +80,9 @@ export class ProtectCamera extends ProtectDevice {
|
|
|
81
80
|
this.log.info("Smart motion detection enabled%s.", this.ufp.featureFlags.smartDetectTypes.length ?
|
|
82
81
|
": " + this.ufp.featureFlags.smartDetectTypes.sort().join(", ") : "");
|
|
83
82
|
}
|
|
83
|
+
if (this.hints.apiStreaming) {
|
|
84
|
+
this.log.warn("API streaming enabled. This feature is experimental and provided without support. Any related support requests will be closed without comment.");
|
|
85
|
+
}
|
|
84
86
|
// Configure accessory information.
|
|
85
87
|
this.configureInfo();
|
|
86
88
|
// Configure MQTT services.
|
|
@@ -100,8 +102,6 @@ export class ProtectCamera extends ProtectDevice {
|
|
|
100
102
|
await this.configureVideoStream();
|
|
101
103
|
// Configure our camera details.
|
|
102
104
|
this.configureCameraDetails();
|
|
103
|
-
// Configure our bitrate switch.
|
|
104
|
-
this.configureDynamicBitrateSwitch();
|
|
105
105
|
// Configure our NVR recording switches.
|
|
106
106
|
this.configureNvrRecordingSwitch();
|
|
107
107
|
// Configure the doorbell trigger.
|
|
@@ -277,9 +277,7 @@ export class ProtectCamera extends ProtectDevice {
|
|
|
277
277
|
else {
|
|
278
278
|
// If the doorbell ring event is still going, we should be as well.
|
|
279
279
|
if (this.isRinging) {
|
|
280
|
-
setTimeout(() =>
|
|
281
|
-
triggerService?.updateCharacteristic(this.hap.Characteristic.On, true);
|
|
282
|
-
}, 50);
|
|
280
|
+
setTimeout(() => triggerService?.updateCharacteristic(this.hap.Characteristic.On, true), 50);
|
|
283
281
|
}
|
|
284
282
|
}
|
|
285
283
|
});
|
|
@@ -379,12 +377,12 @@ export class ProtectCamera extends ProtectDevice {
|
|
|
379
377
|
// Enable RTSP on the camera if needed and get the list of RTSP streams we have ultimately configured.
|
|
380
378
|
this.ufp = await this.nvr.ufpApi.enableRtsp(this.ufp) ?? this.ufp;
|
|
381
379
|
// Figure out which camera channels are RTSP-enabled, and user-enabled.
|
|
382
|
-
let cameraChannels = this.ufp.channels.filter(x => x.isRtspEnabled
|
|
380
|
+
let cameraChannels = this.ufp.channels.filter(x => x.isRtspEnabled);
|
|
383
381
|
// Make sure we've got a HomeKit compatible IDR frame interval. If not, let's take care of that.
|
|
384
|
-
const idrChannels = cameraChannels.filter(x => x.idrInterval !==
|
|
382
|
+
const idrChannels = cameraChannels.filter(x => x.idrInterval !== (PROTECT_HKSV_IDR_INTERVAL / 2));
|
|
385
383
|
if (idrChannels.length) {
|
|
386
384
|
// Edit the channel map and update the Protect controller.
|
|
387
|
-
this.ufp = await this.nvr.ufpApi.updateDevice(this.ufp, { channels: idrChannels.map(x => Object.assign(x, { idrInterval:
|
|
385
|
+
this.ufp = await this.nvr.ufpApi.updateDevice(this.ufp, { channels: idrChannels.map(x => Object.assign(x, { idrInterval: (PROTECT_HKSV_IDR_INTERVAL / 2) })) }) ??
|
|
388
386
|
this.ufp;
|
|
389
387
|
}
|
|
390
388
|
// Set the camera and shapshot URLs.
|
|
@@ -393,9 +391,8 @@ export class ProtectCamera extends ProtectDevice {
|
|
|
393
391
|
cameraChannels = cameraChannels.filter(x => x.name !== "Package Camera");
|
|
394
392
|
// No RTSP streams are available that meet our criteria - we're done.
|
|
395
393
|
if (!cameraChannels.length) {
|
|
396
|
-
this.log.info("No RTSP
|
|
397
|
-
"
|
|
398
|
-
"assign the Administrator role to the user configured for this plugin to allow it to automatically configure itself.");
|
|
394
|
+
this.log.info("No RTSP streaming profiles have been configured for this camera. You can resolve this issue by either enabling at least one RTSP profile in the " +
|
|
395
|
+
"UniFi Protect webUI or assigning an admin role to the local Protect user account you configured for this plugin.");
|
|
399
396
|
return false;
|
|
400
397
|
}
|
|
401
398
|
// Now that we have our RTSP streams, create a list of supported resolutions for HomeKit.
|
|
@@ -494,24 +491,24 @@ export class ProtectCamera extends ProtectDevice {
|
|
|
494
491
|
for (const rtspProfile of ["LOW", "MEDIUM", "HIGH"]) {
|
|
495
492
|
// Check to see if the user has requested a specific streaming profile for this camera.
|
|
496
493
|
if (this.hasFeature("Video.Stream.Only." + rtspProfile)) {
|
|
497
|
-
this.
|
|
494
|
+
this.hints.streamingDefault = rtspProfile;
|
|
498
495
|
}
|
|
499
496
|
// Check to see if the user has requested a specific recording profile for this camera.
|
|
500
497
|
if (this.hasFeature("Video.HKSV.Record.Only." + rtspProfile)) {
|
|
501
|
-
this.
|
|
498
|
+
this.hints.recordingDefault = rtspProfile;
|
|
502
499
|
}
|
|
503
500
|
}
|
|
504
501
|
// Inform the user if we've set a streaming default.
|
|
505
|
-
if (this.
|
|
506
|
-
this.log.info("Video streaming configured to use only: %s.", toCamelCase(this.
|
|
502
|
+
if (this.hints.streamingDefault) {
|
|
503
|
+
this.log.info("Video streaming configured to use only: %s.", toCamelCase(this.hints.streamingDefault.toLowerCase()));
|
|
507
504
|
}
|
|
508
505
|
// Inform the user if they've selected the legacy snapshot API.
|
|
509
506
|
if (!this.hints.highResSnapshots) {
|
|
510
507
|
this.log.info("Disabling the use of higher quality snapshots.");
|
|
511
508
|
}
|
|
512
509
|
// Inform the user if we've set a recording default.
|
|
513
|
-
if (this.
|
|
514
|
-
this.log.info("HomeKit Secure Video event recording configured to use only: %s.", toCamelCase(this.
|
|
510
|
+
if (this.hints.recordingDefault) {
|
|
511
|
+
this.log.info("HomeKit Secure Video event recording configured to use only: %s.", toCamelCase(this.hints.recordingDefault.toLowerCase()));
|
|
515
512
|
}
|
|
516
513
|
// Configure the video stream with our resolutions.
|
|
517
514
|
this.stream = new ProtectStreamingDelegate(this, this.rtspEntries.map(x => x.resolution));
|
|
@@ -566,60 +563,6 @@ export class ProtectCamera extends ProtectDevice {
|
|
|
566
563
|
this.log.info("Enabling HKSV recording switch.");
|
|
567
564
|
return true;
|
|
568
565
|
}
|
|
569
|
-
// Configure a switch to manually enable or disable dynamic bitrate capabilities for a camera.
|
|
570
|
-
configureDynamicBitrateSwitch() {
|
|
571
|
-
// Validate whether we should have this service enabled.
|
|
572
|
-
if (!this.validService(this.hap.Service.Switch, () => {
|
|
573
|
-
// If we don't want a dynamic bitrate switch, disable it and we're done.
|
|
574
|
-
if (!this.hasFeature("Video.DynamicBitrate.Switch")) {
|
|
575
|
-
return false;
|
|
576
|
-
}
|
|
577
|
-
return true;
|
|
578
|
-
}, ProtectReservedNames.SWITCH_DYNAMIC_BITRATE)) {
|
|
579
|
-
// We want to default this back to off by default whenever we disable the dynamic bitrate switch.
|
|
580
|
-
this.accessory.context.dynamicBitrate = false;
|
|
581
|
-
return false;
|
|
582
|
-
}
|
|
583
|
-
// Acquire the service.
|
|
584
|
-
const service = this.acquireService(this.hap.Service.Switch, this.accessoryName + " Dynamic Bitrate", ProtectReservedNames.SWITCH_DYNAMIC_BITRATE);
|
|
585
|
-
// Fail gracefully.
|
|
586
|
-
if (!service) {
|
|
587
|
-
this.log.error("Unable to add dynamic bitrate switch.");
|
|
588
|
-
return false;
|
|
589
|
-
}
|
|
590
|
-
// Activate or deactivate dynamic bitrate for this device.
|
|
591
|
-
service.getCharacteristic(this.hap.Characteristic.On)?.onGet(() => {
|
|
592
|
-
return this.accessory.context.dynamicBitrate ?? false;
|
|
593
|
-
});
|
|
594
|
-
service.getCharacteristic(this.hap.Characteristic.On)?.onSet(async (value) => {
|
|
595
|
-
if (this.accessory.context.dynamicBitrate === value) {
|
|
596
|
-
return;
|
|
597
|
-
}
|
|
598
|
-
// We're enabling dynamic bitrate for this device.
|
|
599
|
-
if (value) {
|
|
600
|
-
this.accessory.context.dynamicBitrate = true;
|
|
601
|
-
this.log.info("Dynamic streaming bitrate adjustment on the UniFi Protect controller enabled.");
|
|
602
|
-
return;
|
|
603
|
-
}
|
|
604
|
-
// We're disabling dynamic bitrate for this device. Update the channels JSON to revert to the maximum bitrate we can.
|
|
605
|
-
const updatedChannels = this.ufp.channels.map(channel => ({ ...channel, bitrate: channel.maxBitrate }));
|
|
606
|
-
// Send the channels JSON to Protect.
|
|
607
|
-
const newDevice = await this.nvr.ufpApi.updateDevice(this.ufp, { channels: updatedChannels });
|
|
608
|
-
// We failed.
|
|
609
|
-
if (!newDevice) {
|
|
610
|
-
this.log.error("Unable to set the streaming bitrate to %s.", value);
|
|
611
|
-
}
|
|
612
|
-
else {
|
|
613
|
-
this.ufp = newDevice;
|
|
614
|
-
}
|
|
615
|
-
this.accessory.context.dynamicBitrate = false;
|
|
616
|
-
this.log.info("Dynamic streaming bitrate adjustment on the UniFi Protect controller disabled.");
|
|
617
|
-
});
|
|
618
|
-
// Initialize the switch.
|
|
619
|
-
service.updateCharacteristic(this.hap.Characteristic.On, this.accessory.context.dynamicBitrate);
|
|
620
|
-
this.log.info("Enabling the dynamic streaming bitrate adjustment switch.");
|
|
621
|
-
return true;
|
|
622
|
-
}
|
|
623
566
|
// Configure a series of switches to manually enable or disable recording on the UniFi Protect controller for a camera.
|
|
624
567
|
configureNvrRecordingSwitch() {
|
|
625
568
|
const switchesEnabled = [];
|
|
@@ -652,9 +595,7 @@ export class ProtectCamera extends ProtectDevice {
|
|
|
652
595
|
// We only want to do something if we're being activated. Turning off the switch would really be an undefined state given that there are three different
|
|
653
596
|
// settings one can choose from. Instead, we do nothing and leave it to the user to choose what state they really want to set.
|
|
654
597
|
if (!value) {
|
|
655
|
-
setTimeout(() =>
|
|
656
|
-
this.updateDevice();
|
|
657
|
-
}, 50);
|
|
598
|
+
setTimeout(() => this.updateDevice(), 50);
|
|
658
599
|
return;
|
|
659
600
|
}
|
|
660
601
|
// Set our recording mode.
|
|
@@ -738,45 +679,16 @@ export class ProtectCamera extends ProtectDevice {
|
|
|
738
679
|
}
|
|
739
680
|
return true;
|
|
740
681
|
}
|
|
741
|
-
// Get the current bitrate for a specific camera channel.
|
|
742
|
-
getBitrate(channelId) {
|
|
743
|
-
// Find the right channel.
|
|
744
|
-
const channel = this.ufp.channels.find(x => x.id === channelId);
|
|
745
|
-
return channel?.bitrate ?? -1;
|
|
746
|
-
}
|
|
747
|
-
// Set the bitrate for a specific camera channel.
|
|
748
|
-
async setBitrate(channelId, value) {
|
|
749
|
-
// If we've disabled the ability to set the bitrate dynamically, silently fail. We prioritize switches over the global setting here, in case the user enabled both,
|
|
750
|
-
// using the principle that the most specific setting always wins. If the user has both the global setting and the switch enabled, the switch will take precedence.
|
|
751
|
-
if ((!this.accessory.context.dynamicBitrate && !this.hasFeature("Video.DynamicBitrate")) ||
|
|
752
|
-
(!this.accessory.context.dynamicBitrate && this.hasFeature("Video.DynamicBitrate") && this.hasFeature("Video.DynamicBitrate.Switch"))) {
|
|
753
|
-
return true;
|
|
754
|
-
}
|
|
755
|
-
// Find the right channel.
|
|
756
|
-
const channel = this.ufp.channels.find(x => x.id === channelId);
|
|
757
|
-
// No channel, we're done.
|
|
758
|
-
if (!channel) {
|
|
759
|
-
return false;
|
|
760
|
-
}
|
|
761
|
-
// If our correct bitrate is already set, we're done.
|
|
762
|
-
if (channel.bitrate === value) {
|
|
763
|
-
return true;
|
|
764
|
-
}
|
|
765
|
-
// Make sure the requested bitrate fits within the constraints of what this channel can do.
|
|
766
|
-
channel.bitrate = Math.min(channel.maxBitrate, Math.max(channel.minBitrate, value));
|
|
767
|
-
// Tell Protect about it.
|
|
768
|
-
const newDevice = await this.nvr.ufpApi.updateDevice(this.ufp, { channels: this.ufp.channels });
|
|
769
|
-
if (!newDevice) {
|
|
770
|
-
this.log.error("Unable to set the streaming bitrate to %s.", value);
|
|
771
|
-
return false;
|
|
772
|
-
}
|
|
773
|
-
// Save our updated device context.
|
|
774
|
-
this.ufp = newDevice;
|
|
775
|
-
return true;
|
|
776
|
-
}
|
|
777
682
|
// Find an RTSP configuration for a given target resolution.
|
|
778
683
|
findRtspEntry(width, height, options) {
|
|
779
684
|
const rtspEntries = options?.rtspEntries ?? this.rtspEntries;
|
|
685
|
+
// Dynamically add codec information to the stream description.
|
|
686
|
+
const formatEntry = (entry) => {
|
|
687
|
+
if (!entry) {
|
|
688
|
+
return null;
|
|
689
|
+
}
|
|
690
|
+
return Object.assign({}, entry, { name: entry.name + " [" + (this.ufp.videoCodec.replace("h265", "hevc")).toUpperCase() + "]" });
|
|
691
|
+
};
|
|
780
692
|
// No RTSP entries to choose from, we're done.
|
|
781
693
|
if (!rtspEntries || !rtspEntries.length) {
|
|
782
694
|
return null;
|
|
@@ -784,39 +696,38 @@ export class ProtectCamera extends ProtectDevice {
|
|
|
784
696
|
// Second, we check to see if we've set an explicit preference for stream quality.
|
|
785
697
|
if (options?.default) {
|
|
786
698
|
options.default = options.default.toUpperCase();
|
|
787
|
-
return rtspEntries.find(x => x.channel.name.toUpperCase() === options.default) ?? null;
|
|
699
|
+
return formatEntry(rtspEntries.find(x => x.channel.name.toUpperCase() === options.default)) ?? null;
|
|
788
700
|
}
|
|
789
701
|
// See if we have a match for our desired resolution on the camera. We ignore FPS - HomeKit clients seem to be able to handle it just fine.
|
|
790
702
|
const exactRtsp = rtspEntries.find(x => (x.channel.width === width) && (x.channel.height === height));
|
|
791
703
|
if (exactRtsp) {
|
|
792
|
-
return exactRtsp;
|
|
704
|
+
return formatEntry(exactRtsp);
|
|
793
705
|
}
|
|
794
706
|
// If we haven't found an exact match, by default, we bias ourselves to the next lower resolution we find or the lowest resolution we have available as a backstop.
|
|
795
707
|
if (!options?.biasHigher) {
|
|
796
|
-
return rtspEntries.find(x => x.channel.width < width) ?? rtspEntries[rtspEntries.length - 1];
|
|
708
|
+
return formatEntry(rtspEntries.find(x => x.channel.width < width) ?? rtspEntries[rtspEntries.length - 1]);
|
|
797
709
|
}
|
|
798
710
|
// If we're biasing ourselves toward higher resolutions (primarily used when transcoding so we start with a higher quality input), we look for the first entry that's
|
|
799
711
|
// larger than our requested width and if not found, we return the highest resolution we have available.
|
|
800
|
-
return rtspEntries.filter(x => x.channel.width > width).pop() ?? rtspEntries[0];
|
|
712
|
+
return formatEntry(rtspEntries.filter(x => x.channel.width > width).pop() ?? rtspEntries[0]);
|
|
801
713
|
}
|
|
802
714
|
// Find a streaming RTSP configuration for a given target resolution.
|
|
803
715
|
findRtsp(width, height, options) {
|
|
804
716
|
// Create our options JSON if needed.
|
|
805
717
|
options = options ?? {};
|
|
806
718
|
// Set our default stream, if we've configured one.
|
|
807
|
-
options.default = this.
|
|
719
|
+
options.default = this.hints.streamingDefault;
|
|
808
720
|
// See if we've been given RTSP entries or whether we should default to our own.
|
|
809
721
|
options.rtspEntries = options.rtspEntries ?? this.rtspEntries;
|
|
810
722
|
// If we've imposed a constraint on the maximum dimensions of what we want due to a hardware limitation, filter out those entries.
|
|
811
723
|
if (options.maxPixels !== undefined) {
|
|
812
724
|
options.rtspEntries = options.rtspEntries.filter(x => (x.channel.width * x.channel.height) <= (options.maxPixels ?? Infinity));
|
|
813
725
|
}
|
|
814
|
-
// Return the entry.
|
|
815
726
|
return this.findRtspEntry(width, height, options);
|
|
816
727
|
}
|
|
817
728
|
// Find a recording RTSP configuration for a given target resolution.
|
|
818
729
|
findRecordingRtsp(width, height) {
|
|
819
|
-
return this.findRtspEntry(width, height, { biasHigher: true, default: this.
|
|
730
|
+
return this.findRtspEntry(width, height, { biasHigher: true, default: this.hints.recordingDefault ?? this.stream.ffmpegOptions.recordingDefaultChannel });
|
|
820
731
|
}
|
|
821
732
|
// Utility function for sorting by resolution.
|
|
822
733
|
sortByResolutions(a, b) {
|