homebridge-unifi-protect 6.8.1 → 6.9.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 -16
- package/config.schema.json +0 -24
- package/dist/protect-camera-package.d.ts +9 -0
- package/dist/protect-camera-package.js +120 -0
- package/dist/protect-camera-package.js.map +1 -0
- package/dist/protect-camera.d.ts +5 -12
- package/dist/protect-camera.js +43 -134
- package/dist/protect-camera.js.map +1 -1
- package/dist/protect-device.d.ts +7 -1
- package/dist/protect-device.js +56 -5
- package/dist/protect-device.js.map +1 -1
- package/dist/protect-doorbell.d.ts +4 -0
- package/dist/protect-doorbell.js +54 -0
- package/dist/protect-doorbell.js.map +1 -1
- package/dist/protect-ffmpeg-codecs.js +5 -4
- package/dist/protect-ffmpeg-codecs.js.map +1 -1
- package/dist/protect-ffmpeg-options.d.ts +2 -2
- package/dist/protect-ffmpeg-options.js +199 -149
- package/dist/protect-ffmpeg-options.js.map +1 -1
- package/dist/protect-ffmpeg-record.js +1 -1
- package/dist/protect-ffmpeg-record.js.map +1 -1
- package/dist/protect-ffmpeg.d.ts +3 -3
- package/dist/protect-ffmpeg.js.map +1 -1
- package/dist/protect-nvr-events.js +28 -29
- package/dist/protect-nvr-events.js.map +1 -1
- package/dist/protect-nvr-systeminfo.js +1 -1
- package/dist/protect-nvr-systeminfo.js.map +1 -1
- package/dist/protect-nvr.d.ts +2 -3
- package/dist/protect-nvr.js +22 -101
- package/dist/protect-nvr.js.map +1 -1
- package/dist/protect-options.d.ts +4 -3
- package/dist/protect-options.js +84 -86
- package/dist/protect-options.js.map +1 -1
- package/dist/protect-platform.js +3 -13
- package/dist/protect-platform.js.map +1 -1
- package/dist/protect-record.js +7 -6
- package/dist/protect-record.js.map +1 -1
- package/dist/protect-sensor.d.ts +6 -3
- package/dist/protect-sensor.js +65 -13
- package/dist/protect-sensor.js.map +1 -1
- package/dist/protect-stream.d.ts +2 -2
- package/dist/protect-stream.js +18 -27
- package/dist/protect-stream.js.map +1 -1
- package/dist/protect-timeshift.d.ts +1 -1
- package/dist/protect-timeshift.js +2 -2
- package/dist/protect-timeshift.js.map +1 -1
- package/dist/protect-viewer.d.ts +2 -1
- package/dist/protect-viewer.js +12 -0
- package/dist/protect-viewer.js.map +1 -1
- package/homebridge-ui/public/index.html +236 -39
- package/homebridge-ui/server.js +2 -2
- package/package.json +7 -7
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
</DIV>
|
|
14
14
|
</SPAN>
|
|
15
15
|
|
|
16
|
-
`homebridge-unifi-protect` is a [Homebridge](https://homebridge.io) plugin that provides HomeKit support to the [UniFi Protect](https://
|
|
16
|
+
`homebridge-unifi-protect` is a [Homebridge](https://homebridge.io) plugin that provides HomeKit support to the [UniFi Protect](https://ui.com/us/camera-security) device ecosystem. [UniFi Protect](https://ui.com/us/camera-security) is [Ubiquiti's](https://www.ui.com) video security platform, with rich camera, doorbell, and NVR controller hardware options for you to choose from, as well as an app which you can use to view, configure and manage your video camera and doorbells.
|
|
17
17
|
|
|
18
18
|
## <A NAME="why"></A>Why Use This Plugin For UniFi Protect Support In HomeKit?
|
|
19
19
|
This plugin attempts to bridge a gap in the UniFi Protect ecosystem by providing native HomeKit support on par with what you would expect from a first-party of native HomeKit solution. My north star is to create a plugin that *just works* with minimal required configuration by you to get up and running. The goal is to provide as close to a streamlined experience as you would expect from a first-party or native HomeKit solution. For the adventurous, there are more granular options available to enable you to further tailor your experience.
|
|
@@ -23,13 +23,13 @@ What does *just works* mean in practice? It means that this plugin will discover
|
|
|
23
23
|
For the more technically inclined - this plugin has continued to pioneer the HomeKit user experience for UniFi Protect by being the ***first*** Homebridge plugin (and first third-party app, to my knowledge) to successfully reverse engineer the UniFi Protect realtime events API that was introduced with UniFi OS. This allows instantaneous, realtime capturing of events as they occur in the Protect ecosystem, allowing us to provide that same level of realtime sensor and camera feedback to HomeKit. Since reverse engineering the realtime events API, most of the major open source smart automation projects have benefited and also incorporated our work, improving the experience for everyone across smart home ecosystems.
|
|
24
24
|
|
|
25
25
|
### Features
|
|
26
|
-
- ***Easy* configuration - all you need is your UniFi Protect controller IP address, username, and password to get started.** The defaults work for the vast majority of users. When you want more, there are [additional options](https://github.com/hjdhjd/homebridge-unifi-protect/blob/main/docs/FeatureOptions.md) you can play with, if you choose.
|
|
26
|
+
- ***Easy* configuration - all you need is your UniFi Protect controller IP address, username, and password to get started.** The defaults work quite well for the vast majority of users. When you want more, there are [additional options](https://github.com/hjdhjd/homebridge-unifi-protect/blob/main/docs/FeatureOptions.md) you can play with, if you choose.
|
|
27
27
|
|
|
28
28
|
- **Full HomeKit support for the UniFi Protect ecosystem.** All generally available UniFi Protect devices are supported, including cameras, chimes, doorbells, lights, sensors, and ViewPorts.
|
|
29
29
|
|
|
30
30
|
- **[Complete HomeKit Secure Video support for all UniFi Protect cameras.](https://github.com/hjdhjd/homebridge-unifi-protect/blob/main/docs/HomeKitSecureVideo.md)** Complete HomeKit Secure Video support, without the need for additional plugins or software beyond FFmpeg. Another community first - all without the need for additional tools to get a complete solution.
|
|
31
31
|
|
|
32
|
-
- **Blazing fast video streaming.** Video streaming from HomeKit will start within in 1-2 seconds for cameras, in most cases - less than a second when using a fast machine like an Apple Silicon Mac mini. I've spent the time to optimize the video streaming experience to ensure it feels very responsive, and *just works*.
|
|
32
|
+
- **Blazing fast video streaming.** Video streaming from HomeKit will start within in 1-2 seconds for cameras, in most cases - less than a second when using a fast machine like an Apple Silicon Mac mini. I've spent the time to optimize the video streaming experience to ensure it feels very responsive, and *just works*. For those that have hardware-accelerated CPUs and GPUs, streaming load times that can be as low as 0.2-0.3 seconds on a day-to-day basis, which is often better than the native UniFi Protect app! Supported hardware-accelerated platforms are currently: Apple Macs (both Intel and Apple Silicon), Intel Quick Sync Video-enabled CPUs, and Raspberry Pi 4.
|
|
33
33
|
|
|
34
34
|
- **[Full UniFi Protect Doorbell support.](https://github.com/hjdhjd/homebridge-unifi-protect/blob/main/docs/Doorbell.md).** This plugin provides complete support for [UniFi Protect Doorbells](https://store.ui.com/collections/unifi-protect/products/uvc-g4-doorbell). We support all the features of the doorbell including - doorbell rings, two-way audio, package camera support, and the use of the onboard LCD screen for messages. Two-way audio has caveats you should be aware of.
|
|
35
35
|
|
|
@@ -88,19 +88,6 @@ Audio on cameras is tricky in the HomeKit world to begin with, and when you thro
|
|
|
88
88
|
|
|
89
89
|
* **Audio support will not work unless you have a version of FFmpeg that supports fdk-aac.** Unfortunately, most default installations of FFmpeg are not compiled with support for fdk-aac. You'll need to compile or acquire a version of FFmpeg that does. Doing so is beyond the scope of this documentation. There are plenty of guides to this - Google is your friend. This plugin uses [ffmpeg-for-homebridge](https://www.npmjs.com/package/ffmpeg-for-homebridge) which eases the pain somewhat by providing prebuilt static binaries of FFmpeg for certain platforms, and save you the trouble of having to compile a version of FFmpeg yourself.
|
|
90
90
|
|
|
91
|
-
### Using Other Video Processors
|
|
92
|
-
`videoProcessor` is the video processor used to stream video. By default, this is [FFmpeg](https://ffmpeg.org), but can be your own custom version of ffmpeg or other video processor that accepts and understands FFmpeg command line semantics.
|
|
93
|
-
|
|
94
|
-
```
|
|
95
|
-
{
|
|
96
|
-
"platform": "UniFi Protect",
|
|
97
|
-
"videoProcessor": "/my/own/compiled/ffmpeg",
|
|
98
|
-
"controllers": [
|
|
99
|
-
...
|
|
100
|
-
]
|
|
101
|
-
}
|
|
102
|
-
```
|
|
103
|
-
|
|
104
91
|
### Things To Be Aware Of
|
|
105
92
|
- **Make sure you are running on the latest production / stable firmwares for both your controller platform (UCKgen2+, UDM-Pro, UNVR, etc.) as well as the latest production / stable UniFi Protect controller firmware.**
|
|
106
93
|
- **No beta versions of iOS, iPadOS, macOS, tvOS, or watchOS are supported. You are on your own if you choose to install / run beta firmwares - don't expect support or sympathy if you run into issues.**
|
package/config.schema.json
CHANGED
|
@@ -143,28 +143,6 @@
|
|
|
143
143
|
}
|
|
144
144
|
},
|
|
145
145
|
|
|
146
|
-
"occupancyDuration": {
|
|
147
|
-
|
|
148
|
-
"type": "integer",
|
|
149
|
-
"title": "Occupancy Event Duration (seconds)",
|
|
150
|
-
"required": false,
|
|
151
|
-
"minimum": 60,
|
|
152
|
-
"maximum": 3600,
|
|
153
|
-
"placeholder": "e.g. 300",
|
|
154
|
-
"description": "Duration of time to wait without receiving a motion event to determine when occupancy is no longer detected. Default: 300."
|
|
155
|
-
},
|
|
156
|
-
|
|
157
|
-
"motionDuration": {
|
|
158
|
-
|
|
159
|
-
"type": "integer",
|
|
160
|
-
"title": "Motion Event Duration (seconds)",
|
|
161
|
-
"required": false,
|
|
162
|
-
"minimum": 5,
|
|
163
|
-
"maximum": 300,
|
|
164
|
-
"placeholder": "e.g. 10",
|
|
165
|
-
"description": "Duration of a single motion event, before allowing a new motion event. Setting this too low will potentially cause a lot of excess notifications. Default: 10."
|
|
166
|
-
},
|
|
167
|
-
|
|
168
146
|
"ringDuration": {
|
|
169
147
|
|
|
170
148
|
"type": "integer",
|
|
@@ -318,8 +296,6 @@
|
|
|
318
296
|
"videoProcessor",
|
|
319
297
|
"videoEncoder",
|
|
320
298
|
"verboseFfmpeg",
|
|
321
|
-
"motionDuration",
|
|
322
|
-
"occupancyDuration",
|
|
323
299
|
"ringDuration"
|
|
324
300
|
]
|
|
325
301
|
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { ProtectCamera, RtspEntry } from "./protect-camera.js";
|
|
2
|
+
export declare class ProtectCameraPackage extends ProtectCamera {
|
|
3
|
+
protected configureDevice(): Promise<boolean>;
|
|
4
|
+
get id(): string;
|
|
5
|
+
findRtsp(): RtspEntry | null;
|
|
6
|
+
findRecordingRtsp(): RtspEntry | null;
|
|
7
|
+
getBitrate(channelId: number): number;
|
|
8
|
+
setBitrate(): Promise<boolean>;
|
|
9
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/* Copyright(C) 2019-2023, HJD (https://github.com/hjdhjd). All rights reserved.
|
|
2
|
+
*
|
|
3
|
+
* protect-camera-package.ts: Package camera device class for UniFi Protect.
|
|
4
|
+
*/
|
|
5
|
+
import { ProtectCamera } from "./protect-camera.js";
|
|
6
|
+
import { ProtectStreamingDelegate } from "./protect-stream.js";
|
|
7
|
+
// Package camera class. To avoid circular dependencies, this has to be declared in the same file as ProtectCamera, given the ProtectCamera class references it.
|
|
8
|
+
export class ProtectCameraPackage extends ProtectCamera {
|
|
9
|
+
// Configure the package camera.
|
|
10
|
+
async configureDevice() {
|
|
11
|
+
// Get our parent camera.
|
|
12
|
+
const parentCamera = this.nvr.deviceLookup(this.ufp.id);
|
|
13
|
+
this.hasHksv = true;
|
|
14
|
+
this.hints.probesize = 32768;
|
|
15
|
+
if (parentCamera) {
|
|
16
|
+
this.hints.timeshift = parentCamera.hints.timeshift;
|
|
17
|
+
this.hints.hardwareDecoding = parentCamera.hints.hardwareDecoding;
|
|
18
|
+
this.hints.hardwareTranscoding = parentCamera.hints.hardwareTranscoding;
|
|
19
|
+
this.hints.logHksv = parentCamera.hints.logHksv;
|
|
20
|
+
}
|
|
21
|
+
// Clean out the context object in case it's been polluted somehow.
|
|
22
|
+
this.accessory.context = {};
|
|
23
|
+
// Inherit our HKSV and motion awareness states from our parent camera.
|
|
24
|
+
this.accessory.context.hksvRecording = parentCamera?.accessory.context.hksvRecording;
|
|
25
|
+
this.accessory.context.detectMotion = parentCamera?.accessory.context.detectMotion;
|
|
26
|
+
// We explicitly avoid adding the MAC address of the camera - that's reserved for real Protect devices, not synthetic ones we create.
|
|
27
|
+
this.accessory.context.nvr = this.nvr.ufp.mac;
|
|
28
|
+
this.accessory.context.packageCamera = this.ufp.mac;
|
|
29
|
+
// Configure accessory information.
|
|
30
|
+
this.configureInfo();
|
|
31
|
+
// Configure the motion sensor.
|
|
32
|
+
this.configureMotionSensor();
|
|
33
|
+
// Set the snapshot URL.
|
|
34
|
+
this.snapshotUrl = this.nvr.ufpApi.getApiEndpoint(this.ufp.modelKey) + "/" + this.ufp.id + "/package-snapshot";
|
|
35
|
+
let hkResolutions = [];
|
|
36
|
+
const validResolutions = [this.findRtsp()?.resolution ?? [1600, 1200, 2]];
|
|
37
|
+
// Ensure we have mandatory resolutions required by HomeKit, as well as special support for Apple TV and Apple Watch, while respecting aspect ratios.
|
|
38
|
+
// We use the frame rate of the first entry, which should be our highest resolution option that's native to the camera as the upper bound for frame rate.
|
|
39
|
+
//
|
|
40
|
+
// Our supported resolutions range from 4K through 320p...even for package cameras.
|
|
41
|
+
if ((validResolutions[0][0] / validResolutions[0][1]) === (16 / 9)) {
|
|
42
|
+
hkResolutions = [
|
|
43
|
+
[3840, 2160, 15], [2560, 1440, 15],
|
|
44
|
+
[1920, 1080, 15], [1280, 720, 15],
|
|
45
|
+
[640, 360, 15], [480, 270, 15],
|
|
46
|
+
[320, 180, 15]
|
|
47
|
+
];
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
hkResolutions = [
|
|
51
|
+
[3840, 2880, 15], [2560, 1920, 15],
|
|
52
|
+
[1920, 1440, 15], [1280, 960, 15],
|
|
53
|
+
[640, 480, 15], [480, 360, 15],
|
|
54
|
+
[320, 240, 15]
|
|
55
|
+
];
|
|
56
|
+
}
|
|
57
|
+
// Validate and add our entries to the list of what we make available to HomeKit.
|
|
58
|
+
for (const entry of hkResolutions) {
|
|
59
|
+
// This resolution is larger than the highest resolution on the camera, natively. We make an exception for
|
|
60
|
+
// 1080p and 720p resolutions since HomeKit explicitly requires them.
|
|
61
|
+
if ((entry[0] >= validResolutions[0][0]) && ![1920, 1280].includes(entry[0])) {
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
// We already have this resolution in our list.
|
|
65
|
+
if (validResolutions.some(x => (x[0] === entry[0]) && (x[1] === entry[1]) && (x[2] === entry[2]))) {
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
validResolutions.push(entry);
|
|
69
|
+
}
|
|
70
|
+
// Inform users about our RTSP entry mapping, if we're debugging.
|
|
71
|
+
if (this.nvr.optionEnabled(this.ufp, "Debug.Video.Startup", false)) {
|
|
72
|
+
for (const entry of validResolutions) {
|
|
73
|
+
this.log.info("Mapping resolution: %s.", this.getResolution(entry) + " => " + this.getResolution(validResolutions[0]));
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
// Configure the video stream with our required resolutions. No, package cameras don't really support any of these resolutions, but they're required
|
|
77
|
+
// by HomeKit in order to stream video.
|
|
78
|
+
this.stream = new ProtectStreamingDelegate(this, validResolutions);
|
|
79
|
+
// Fire up the controller and inform HomeKit about it.
|
|
80
|
+
this.accessory.configureController(this.stream.controller);
|
|
81
|
+
// Periodically refresh our snapshot cache.
|
|
82
|
+
void this.configureSnapshotUpdates();
|
|
83
|
+
// We're done.
|
|
84
|
+
return Promise.resolve(true);
|
|
85
|
+
}
|
|
86
|
+
// Return a unique identifier for package cameras based on the parent device's MAC address.
|
|
87
|
+
get id() {
|
|
88
|
+
return this.ufp.mac + ".PackageCamera";
|
|
89
|
+
}
|
|
90
|
+
// Make our RTSP stream findable.
|
|
91
|
+
findRtsp() {
|
|
92
|
+
const channel = this.ufp.channels.find(x => x.name === "Package Camera");
|
|
93
|
+
if (!channel) {
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
// Return the information we need for package camera channel access.
|
|
97
|
+
return {
|
|
98
|
+
channel: channel,
|
|
99
|
+
lens: this.ufp.lenses[0].id,
|
|
100
|
+
name: this.getResolution([channel.width, channel.height, channel.fps]) + " (" + channel.name + ")",
|
|
101
|
+
resolution: [channel.width, channel.height, channel.fps],
|
|
102
|
+
url: "rtsps://" + this.nvr.nvrOptions.address + ":" + this.nvr.ufp.ports.rtsps.toString() + "/" + channel.rtspAlias + "?enableSrtp"
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
// Return a recording RTSP configuration for HKSV.
|
|
106
|
+
findRecordingRtsp() {
|
|
107
|
+
return this.findRtsp();
|
|
108
|
+
}
|
|
109
|
+
// Get the current bitrate for a specific camera channel.
|
|
110
|
+
getBitrate(channelId) {
|
|
111
|
+
// Find the right channel.
|
|
112
|
+
const channel = this.ufp.channels.find(x => x.id === channelId);
|
|
113
|
+
return channel?.bitrate ?? -1;
|
|
114
|
+
}
|
|
115
|
+
// Set the bitrate for a specific camera channel.
|
|
116
|
+
setBitrate() {
|
|
117
|
+
return Promise.resolve(true);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
//# sourceMappingURL=protect-camera-package.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protect-camera-package.js","sourceRoot":"","sources":["../src/protect-camera-package.ts"],"names":[],"mappings":"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"}
|
package/dist/protect-camera.d.ts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import { PlatformAccessory } from "homebridge";
|
|
1
|
+
import { PlatformAccessory, Resolution } from "homebridge";
|
|
2
2
|
import { ProtectCameraChannelConfig, ProtectCameraConfig, ProtectEventPacket } from "unifi-protect";
|
|
3
3
|
import { ProtectDevice } from "./protect-device.js";
|
|
4
4
|
import { ProtectNvr } from "./protect-nvr.js";
|
|
5
5
|
import { ProtectStreamingDelegate } from "./protect-stream.js";
|
|
6
6
|
export interface RtspEntry {
|
|
7
7
|
channel: ProtectCameraChannelConfig;
|
|
8
|
+
lens?: number;
|
|
8
9
|
name: string;
|
|
9
|
-
resolution:
|
|
10
|
+
resolution: Resolution;
|
|
10
11
|
url: string;
|
|
11
12
|
}
|
|
12
13
|
export declare class ProtectCamera extends ProtectDevice {
|
|
@@ -15,7 +16,6 @@ export declare class ProtectCamera extends ProtectDevice {
|
|
|
15
16
|
private isDoorbellConfigured;
|
|
16
17
|
isRinging: boolean;
|
|
17
18
|
private isVideoConfigured;
|
|
18
|
-
packageCamera: ProtectPackageCamera | null;
|
|
19
19
|
private rtspEntries;
|
|
20
20
|
private rtspQuality;
|
|
21
21
|
snapshotUrl: string;
|
|
@@ -33,7 +33,6 @@ export declare class ProtectCamera extends ProtectDevice {
|
|
|
33
33
|
private configureCameraDetails;
|
|
34
34
|
private configureVideoStream;
|
|
35
35
|
protected configureSnapshotUpdates(): Promise<boolean>;
|
|
36
|
-
private configurePackageCamera;
|
|
37
36
|
private configureHksv;
|
|
38
37
|
private configureHksvRecordingSwitch;
|
|
39
38
|
private configureDynamicBitrateSwitch;
|
|
@@ -43,14 +42,8 @@ export declare class ProtectCamera extends ProtectDevice {
|
|
|
43
42
|
getBitrate(channelId: number): number;
|
|
44
43
|
setBitrate(channelId: number, value: number): Promise<boolean>;
|
|
45
44
|
private findRtspEntry;
|
|
46
|
-
findRtsp(width: number, height: number,
|
|
45
|
+
findRtsp(width: number, height: number, rtspEntries?: RtspEntry[], constrainPixels?: number): RtspEntry | null;
|
|
47
46
|
findRecordingRtsp(width: number, height: number, rtspEntries?: RtspEntry[]): RtspEntry | null;
|
|
48
47
|
private sortByResolutions;
|
|
49
|
-
|
|
50
|
-
}
|
|
51
|
-
export declare class ProtectPackageCamera extends ProtectCamera {
|
|
52
|
-
protected configureDevice(): Promise<boolean>;
|
|
53
|
-
findRtsp(): RtspEntry | null;
|
|
54
|
-
getBitrate(channelId: number): number;
|
|
55
|
-
setBitrate(): Promise<boolean>;
|
|
48
|
+
protected getResolution(resolution: Resolution): string;
|
|
56
49
|
}
|
package/dist/protect-camera.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { PROTECT_HOMEKIT_IDR_INTERVAL, PROTECT_SNAPSHOT_CACHE_REFRESH_INTERVAL } from "./settings.js";
|
|
2
2
|
import { ProtectDevice } from "./protect-device.js";
|
|
3
3
|
import { ProtectReservedNames } from "./protect-types.js";
|
|
4
4
|
import { ProtectStreamingDelegate } from "./protect-stream.js";
|
|
@@ -11,7 +11,6 @@ export class ProtectCamera extends ProtectDevice {
|
|
|
11
11
|
this.isDeleted = false;
|
|
12
12
|
this.isRinging = false;
|
|
13
13
|
this.isVideoConfigured = false;
|
|
14
|
-
this.packageCamera = null;
|
|
15
14
|
this.rtspEntries = [];
|
|
16
15
|
this.rtspQuality = {};
|
|
17
16
|
this.ufp = device;
|
|
@@ -95,8 +94,6 @@ export class ProtectCamera extends ProtectDevice {
|
|
|
95
94
|
await this.configureVideoStream();
|
|
96
95
|
// Configure our snapshot updates.
|
|
97
96
|
void this.configureSnapshotUpdates();
|
|
98
|
-
// Configure our package camera.
|
|
99
|
-
this.configurePackageCamera();
|
|
100
97
|
// Configure our camera details.
|
|
101
98
|
this.configureCameraDetails();
|
|
102
99
|
// Configure our bitrate switch.
|
|
@@ -124,10 +121,6 @@ export class ProtectCamera extends ProtectDevice {
|
|
|
124
121
|
// Handle camera-related events.
|
|
125
122
|
eventHandler(packet) {
|
|
126
123
|
const payload = packet.payload;
|
|
127
|
-
// Update the package camera, if we have one.
|
|
128
|
-
if (this.packageCamera) {
|
|
129
|
-
this.packageCamera.ufp = Object.assign({}, this.ufp, { name: this.ufp.name + " Package Camera" });
|
|
130
|
-
}
|
|
131
124
|
// Process any RTSP stream updates.
|
|
132
125
|
if (payload.channels) {
|
|
133
126
|
void this.configureVideoStream();
|
|
@@ -146,9 +139,10 @@ export class ProtectCamera extends ProtectDevice {
|
|
|
146
139
|
this.nvr.events.doorbellEventHandler(this, payload.lastRing);
|
|
147
140
|
}
|
|
148
141
|
// Process camera details updates:
|
|
142
|
+
// - availability state.
|
|
149
143
|
// - camera status light.
|
|
150
144
|
// - camera recording settings.
|
|
151
|
-
if ((payload.ledSettings && ("isEnabled" in payload.ledSettings)) || (payload.recordingSettings && ("mode" in payload.recordingSettings))) {
|
|
145
|
+
if (payload.state || (payload.ledSettings && ("isEnabled" in payload.ledSettings)) || (payload.recordingSettings && ("mode" in payload.recordingSettings))) {
|
|
152
146
|
this.updateDevice();
|
|
153
147
|
}
|
|
154
148
|
}
|
|
@@ -341,7 +335,7 @@ export class ProtectCamera extends ProtectDevice {
|
|
|
341
335
|
// Enable RTSP on the camera if needed and get the list of RTSP streams we have ultimately configured.
|
|
342
336
|
this.ufp = await this.nvr.ufpApi.enableRtsp(this.ufp) ?? this.ufp;
|
|
343
337
|
// Figure out which camera channels are RTSP-enabled, and user-enabled.
|
|
344
|
-
|
|
338
|
+
let cameraChannels = this.ufp.channels.filter(x => x.isRtspEnabled && this.hasFeature("Video.Stream." + x.name));
|
|
345
339
|
// Make sure we've got a HomeKit compatible IDR frame interval. If not, let's take care of that.
|
|
346
340
|
let idrChannels = cameraChannels.filter(x => x.idrInterval !== PROTECT_HOMEKIT_IDR_INTERVAL);
|
|
347
341
|
if (idrChannels.length) {
|
|
@@ -355,6 +349,8 @@ export class ProtectCamera extends ProtectDevice {
|
|
|
355
349
|
// Set the camera and shapshot URLs.
|
|
356
350
|
const cameraUrl = "rtsps://" + (this.nvr.nvrOptions.overrideAddress ?? this.ufp.connectionHost) + ":" + this.nvr.ufp.ports.rtsps.toString() + "/";
|
|
357
351
|
this.snapshotUrl = this.nvr.ufpApi.getApiEndpoint(this.ufp.modelKey) + "/" + this.ufp.id + "/snapshot";
|
|
352
|
+
// Filter out any package camera entries.
|
|
353
|
+
cameraChannels = cameraChannels.filter(x => x.name !== "Package Camera");
|
|
358
354
|
// No RTSP streams are available that meet our criteria - we're done.
|
|
359
355
|
if (!cameraChannels.length) {
|
|
360
356
|
this.log.info("No RTSP stream profiles have been configured for this camera. " +
|
|
@@ -377,29 +373,45 @@ export class ProtectCamera extends ProtectDevice {
|
|
|
377
373
|
}
|
|
378
374
|
// Sort the list of resolutions, from high to low.
|
|
379
375
|
rtspEntries.sort(this.sortByResolutions.bind(this));
|
|
380
|
-
|
|
381
|
-
//
|
|
382
|
-
//
|
|
383
|
-
//
|
|
384
|
-
//
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
376
|
+
let validResolutions = [];
|
|
377
|
+
// Next, ensure we have mandatory resolutions required by HomeKit, as well as special support for Apple TV and Apple Watch, while respecting aspect ratios.
|
|
378
|
+
// We use the frame rate of the first entry, which should be our highest resolution option that's native to the camera as the upper bound for frame rate.
|
|
379
|
+
//
|
|
380
|
+
// Our supported resolutions range from 4K through 320p.
|
|
381
|
+
if ((rtspEntries[0].resolution[0] / rtspEntries[0].resolution[1]) === (16 / 9)) {
|
|
382
|
+
validResolutions = [
|
|
383
|
+
[3840, 2160, 30], [2560, 1440, 30],
|
|
384
|
+
[1920, 1080, 30], [1280, 720, 30],
|
|
385
|
+
[640, 360, 30], [480, 270, 30],
|
|
386
|
+
[320, 180, 30]
|
|
387
|
+
];
|
|
388
|
+
}
|
|
389
|
+
else {
|
|
390
|
+
validResolutions = [
|
|
391
|
+
[3840, 2880, 30], [2560, 1920, 30],
|
|
392
|
+
[1920, 1440, 30], [1280, 960, 30],
|
|
393
|
+
[640, 480, 30], [480, 360, 30],
|
|
394
|
+
[320, 240, 30]
|
|
395
|
+
];
|
|
396
|
+
}
|
|
397
|
+
// Validate and add our entries to the list of what we make available to HomeKit. We map these resolutions to the channels we have available to us on the camera.
|
|
398
|
+
for (const entry of validResolutions) {
|
|
399
|
+
// This resolution is larger than the highest resolution on the camera, natively. We make an exception for
|
|
400
|
+
// 1080p and 720p resolutions since HomeKit explicitly requires them.
|
|
401
|
+
if ((entry[0] >= rtspEntries[0].resolution[0]) && ![1920, 1280].includes(entry[0])) {
|
|
394
402
|
continue;
|
|
395
403
|
}
|
|
396
404
|
// Find the closest RTSP match for this resolution.
|
|
397
|
-
const foundRtsp = this.findRtsp(entry[0], entry[1],
|
|
405
|
+
const foundRtsp = this.findRtsp(entry[0], entry[1], rtspEntries);
|
|
398
406
|
if (!foundRtsp) {
|
|
399
407
|
continue;
|
|
400
408
|
}
|
|
401
|
-
//
|
|
402
|
-
rtspEntries.
|
|
409
|
+
// We already have this resolution in our list.
|
|
410
|
+
if (rtspEntries.some(x => (x.resolution[0] === entry[0]) && (x.resolution[1] === entry[1]) && (x.resolution[2] === foundRtsp.channel.fps))) {
|
|
411
|
+
continue;
|
|
412
|
+
}
|
|
413
|
+
// Add the resolution to the list of supported resolutions, but use the selected camera channel's native frame rate.
|
|
414
|
+
rtspEntries.push({ channel: foundRtsp.channel, name: foundRtsp.name, resolution: [entry[0], entry[1], foundRtsp.channel.fps], url: foundRtsp.url });
|
|
403
415
|
// Since we added resolutions to the list, resort resolutions, from high to low.
|
|
404
416
|
rtspEntries.sort(this.sortByResolutions.bind(this));
|
|
405
417
|
}
|
|
@@ -488,36 +500,6 @@ export class ProtectCamera extends ProtectDevice {
|
|
|
488
500
|
}
|
|
489
501
|
return true;
|
|
490
502
|
}
|
|
491
|
-
// Configure a package camera, if one exists.
|
|
492
|
-
configurePackageCamera() {
|
|
493
|
-
// First, confirm the device has a package camera.
|
|
494
|
-
if (!this.ufp.featureFlags.hasPackageCamera) {
|
|
495
|
-
return false;
|
|
496
|
-
}
|
|
497
|
-
// If we've already setup the package camera, we're done.
|
|
498
|
-
if (this.packageCamera) {
|
|
499
|
-
return true;
|
|
500
|
-
}
|
|
501
|
-
// Generate a UUID for the package camera.
|
|
502
|
-
const uuid = this.hap.uuid.generate(this.ufp.mac + ".PackageCamera");
|
|
503
|
-
// Let's find it if we've already created it.
|
|
504
|
-
let packageCameraAccessory = this.platform.accessories.find((x) => x.UUID === uuid) ?? null;
|
|
505
|
-
// We can't find the accessory. Let's create it.
|
|
506
|
-
if (!packageCameraAccessory) {
|
|
507
|
-
// We will use the NVR MAC address + ".NVRSystemInfo" to create our UUID. That should provide the guaranteed uniqueness we need.
|
|
508
|
-
packageCameraAccessory = new this.api.platformAccessory(this.accessory.displayName + " Package Camera", uuid);
|
|
509
|
-
if (!packageCameraAccessory) {
|
|
510
|
-
this.log.error("Unable to create the package camera accessory.");
|
|
511
|
-
return false;
|
|
512
|
-
}
|
|
513
|
-
// Register this accessory with homebridge and add it to the platform accessory array so we can track it.
|
|
514
|
-
this.api.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [packageCameraAccessory]);
|
|
515
|
-
this.platform.accessories.push(packageCameraAccessory);
|
|
516
|
-
}
|
|
517
|
-
// Now create the package camera accessory. We do want to modify the camera name to ensure things look pretty.
|
|
518
|
-
this.packageCamera = new ProtectPackageCamera(this.nvr, Object.assign({}, this.ufp, { name: this.ufp.name + " Package Camera" }), packageCameraAccessory);
|
|
519
|
-
return true;
|
|
520
|
-
}
|
|
521
503
|
// Configure HomeKit Secure Video support.
|
|
522
504
|
configureHksv() {
|
|
523
505
|
this.hasHksv = true;
|
|
@@ -800,34 +782,11 @@ export class ProtectCamera extends ProtectDevice {
|
|
|
800
782
|
return true;
|
|
801
783
|
}
|
|
802
784
|
// Find an RTSP configuration for a given target resolution.
|
|
803
|
-
findRtspEntry(width, height,
|
|
785
|
+
findRtspEntry(width, height, rtspEntries, defaultStream = this.rtspQuality.StreamingDefault) {
|
|
804
786
|
// No RTSP entries to choose from, we're done.
|
|
805
787
|
if (!rtspEntries || !rtspEntries.length) {
|
|
806
788
|
return null;
|
|
807
789
|
}
|
|
808
|
-
// First, we check to see if we've set an explicit preference for the target address.
|
|
809
|
-
if (address) {
|
|
810
|
-
// If we don't have this address cached, look it up and cache it.
|
|
811
|
-
if (!this.rtspQuality[address]) {
|
|
812
|
-
// Check to see if there's an explicit preference set and cache the result.
|
|
813
|
-
if (this.hasFeature("Video.Stream.Only.Low", address, true)) {
|
|
814
|
-
this.rtspQuality[address] = "LOW";
|
|
815
|
-
}
|
|
816
|
-
else if (this.hasFeature("Video.Stream.Only.Medium", address, true)) {
|
|
817
|
-
this.rtspQuality[address] = "MEDIUM";
|
|
818
|
-
}
|
|
819
|
-
else if (this.hasFeature("Video.Stream.Only.High", address, true)) {
|
|
820
|
-
this.rtspQuality[address] = "HIGH";
|
|
821
|
-
}
|
|
822
|
-
else {
|
|
823
|
-
this.rtspQuality[address] = "None";
|
|
824
|
-
}
|
|
825
|
-
}
|
|
826
|
-
// If it's set to none, we default to our normal lookup logic.
|
|
827
|
-
if (this.rtspQuality[address] !== "None") {
|
|
828
|
-
return rtspEntries.find(x => x.channel.name.toUpperCase() === this.rtspQuality[address]) ?? null;
|
|
829
|
-
}
|
|
830
|
-
}
|
|
831
790
|
// Second, we check to see if we've set an explicit preference for stream quality.
|
|
832
791
|
if (defaultStream) {
|
|
833
792
|
defaultStream = defaultStream.toUpperCase();
|
|
@@ -853,16 +812,16 @@ export class ProtectCamera extends ProtectDevice {
|
|
|
853
812
|
return rtspEntries.find(x => width >= x.resolution[0]) ?? rtspEntries[rtspEntries.length - 1];
|
|
854
813
|
}
|
|
855
814
|
// Find a streaming RTSP configuration for a given target resolution.
|
|
856
|
-
findRtsp(width, height,
|
|
815
|
+
findRtsp(width, height, rtspEntries = this.rtspEntries, constrainPixels = 0) {
|
|
857
816
|
// If we've imposed a constraint on the maximum dimensions of what we want due to a hardware limitation, filter out those entries.
|
|
858
817
|
if (constrainPixels) {
|
|
859
818
|
rtspEntries = rtspEntries.filter(x => (x.channel.width * x.channel.height) <= constrainPixels);
|
|
860
819
|
}
|
|
861
|
-
return this.findRtspEntry(width, height,
|
|
820
|
+
return this.findRtspEntry(width, height, rtspEntries);
|
|
862
821
|
}
|
|
863
822
|
// Find a recording RTSP configuration for a given target resolution.
|
|
864
823
|
findRecordingRtsp(width, height, rtspEntries = this.rtspEntries) {
|
|
865
|
-
return this.findRtspEntry(width, height,
|
|
824
|
+
return this.findRtspEntry(width, height, rtspEntries, this.rtspQuality.RecordingDefault ?? this.stream.ffmpegOptions.recordingDefaultChannel);
|
|
866
825
|
}
|
|
867
826
|
// Utility function for sorting by resolution.
|
|
868
827
|
sortByResolutions(a, b) {
|
|
@@ -894,54 +853,4 @@ export class ProtectCamera extends ProtectDevice {
|
|
|
894
853
|
return resolution[0].toString() + "x" + resolution[1].toString() + "@" + resolution[2].toString() + "fps";
|
|
895
854
|
}
|
|
896
855
|
}
|
|
897
|
-
// Package camera class.
|
|
898
|
-
export class ProtectPackageCamera extends ProtectCamera {
|
|
899
|
-
// Configure the package camera.
|
|
900
|
-
async configureDevice() {
|
|
901
|
-
this.hints.probesize = 32768;
|
|
902
|
-
this.hasHksv = false;
|
|
903
|
-
// Clean out the context object in case it's been polluted somehow.
|
|
904
|
-
this.accessory.context = {};
|
|
905
|
-
// We explicitly avoid adding the MAC address of the camera - that's reserved for real Protect devices, not synthetic ones we create.
|
|
906
|
-
this.accessory.context.nvr = this.nvr.ufp.mac;
|
|
907
|
-
this.accessory.context.packageCamera = this.ufp.mac;
|
|
908
|
-
// Configure accessory information.
|
|
909
|
-
this.configureInfo();
|
|
910
|
-
// Set the snapshot URL.
|
|
911
|
-
this.snapshotUrl = this.nvr.ufpApi.getApiEndpoint(this.ufp.modelKey) + "/" + this.ufp.id + "/package-snapshot";
|
|
912
|
-
// Configure the video stream with our required resolutions. No, package cameras don't really support any of these resolutions, but they're required
|
|
913
|
-
// by HomeKit in order to stream video.
|
|
914
|
-
this.stream = new ProtectStreamingDelegate(this, [[3840, 2160, 30], [1920, 1080, 30], [1280, 960, 30], [1280, 720, 30], [1024, 768, 30], [640, 480, 30],
|
|
915
|
-
[640, 360, 30], [480, 360, 30], [480, 270, 30], [320, 240, 30], [320, 240, 15], [320, 180, 30]]);
|
|
916
|
-
// Fire up the controller and inform HomeKit about it.
|
|
917
|
-
this.accessory.configureController(this.stream.controller);
|
|
918
|
-
// Periodically refresh our snapshot cache.
|
|
919
|
-
void this.configureSnapshotUpdates();
|
|
920
|
-
// We're done.
|
|
921
|
-
return Promise.resolve(true);
|
|
922
|
-
}
|
|
923
|
-
// Make our RTSP stream findable.
|
|
924
|
-
findRtsp() {
|
|
925
|
-
const channel = this.ufp.channels.find(x => x.name === "Package Camera");
|
|
926
|
-
if (!channel) {
|
|
927
|
-
return null;
|
|
928
|
-
}
|
|
929
|
-
return {
|
|
930
|
-
channel: channel,
|
|
931
|
-
name: channel.name,
|
|
932
|
-
resolution: [channel.width, channel.height, channel.fps],
|
|
933
|
-
url: "rtsps://" + this.nvr.nvrOptions.address + ":" + this.nvr.ufp.ports.rtsps.toString() + "/" + channel.rtspAlias + "?enableSrtp"
|
|
934
|
-
};
|
|
935
|
-
}
|
|
936
|
-
// Get the current bitrate for a specific camera channel.
|
|
937
|
-
getBitrate(channelId) {
|
|
938
|
-
// Find the right channel.
|
|
939
|
-
const channel = this.ufp.channels.find(x => x.id === channelId);
|
|
940
|
-
return channel?.bitrate ?? -1;
|
|
941
|
-
}
|
|
942
|
-
// Set the bitrate for a specific camera channel.
|
|
943
|
-
setBitrate() {
|
|
944
|
-
return Promise.resolve(true);
|
|
945
|
-
}
|
|
946
|
-
}
|
|
947
856
|
//# sourceMappingURL=protect-camera.js.map
|