homebridge-unifi-protect 7.20.1 → 7.22.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/dist/devices/protect-camera-package.js +14 -6
- package/dist/devices/protect-camera-package.js.map +1 -1
- package/dist/devices/protect-camera.d.ts +3 -2
- package/dist/devices/protect-camera.js +121 -61
- package/dist/devices/protect-camera.js.map +1 -1
- package/dist/devices/protect-chime.js +3 -2
- package/dist/devices/protect-chime.js.map +1 -1
- package/dist/devices/protect-device.js +19 -22
- package/dist/devices/protect-device.js.map +1 -1
- package/dist/devices/protect-doorbell.d.ts +4 -2
- package/dist/devices/protect-doorbell.js +37 -22
- package/dist/devices/protect-doorbell.js.map +1 -1
- package/dist/devices/protect-light.js +6 -6
- package/dist/devices/protect-light.js.map +1 -1
- package/dist/devices/protect-liveviews.js +8 -26
- package/dist/devices/protect-liveviews.js.map +1 -1
- package/dist/devices/protect-nvr-systeminfo.d.ts +0 -2
- package/dist/devices/protect-nvr-systeminfo.js +13 -47
- package/dist/devices/protect-nvr-systeminfo.js.map +1 -1
- package/dist/devices/protect-securitysystem.js +17 -36
- package/dist/devices/protect-securitysystem.js.map +1 -1
- package/dist/devices/protect-sensor.d.ts +2 -2
- package/dist/devices/protect-sensor.js +16 -16
- package/dist/devices/protect-sensor.js.map +1 -1
- package/dist/devices/protect-viewer.js +8 -8
- package/dist/devices/protect-viewer.js.map +1 -1
- package/dist/protect-events.js +16 -20
- package/dist/protect-events.js.map +1 -1
- package/dist/protect-livestream.d.ts +1 -0
- package/dist/protect-livestream.js +23 -3
- package/dist/protect-livestream.js.map +1 -1
- package/dist/protect-nvr.d.ts +5 -5
- package/dist/protect-nvr.js +36 -34
- package/dist/protect-nvr.js.map +1 -1
- package/dist/protect-options.d.ts +4 -4
- package/dist/protect-options.js +16 -15
- package/dist/protect-options.js.map +1 -1
- package/dist/protect-platform.d.ts +1 -1
- package/dist/protect-platform.js +8 -14
- package/dist/protect-platform.js.map +1 -1
- package/dist/protect-record.d.ts +1 -4
- package/dist/protect-record.js +13 -28
- package/dist/protect-record.js.map +1 -1
- package/dist/protect-snapshot.js.map +1 -1
- package/dist/protect-stream.js +39 -56
- package/dist/protect-stream.js.map +1 -1
- package/dist/protect-timeshift.d.ts +0 -2
- package/dist/protect-timeshift.js +12 -23
- package/dist/protect-timeshift.js.map +1 -1
- package/dist/protect-types.d.ts +1 -0
- package/dist/protect-types.js +1 -0
- package/dist/protect-types.js.map +1 -1
- package/dist/settings.d.ts +4 -4
- package/dist/settings.js +5 -5
- package/dist/settings.js.map +1 -1
- package/homebridge-ui/public/index.html +54 -51
- package/homebridge-ui/public/lib/featureoptions.js +7 -8
- package/homebridge-ui/public/lib/featureoptions.js.map +1 -1
- package/homebridge-ui/public/lib/webUi-featureoptions.mjs +3484 -558
- package/homebridge-ui/public/lib/webUi.mjs +13 -5
- package/homebridge-ui/public/ui.mjs +62 -99
- package/homebridge-ui/server.js +6 -1
- package/package.json +10 -13
|
@@ -30,7 +30,15 @@ export class ProtectCameraPackage extends ProtectCamera {
|
|
|
30
30
|
this.accessory.context = {};
|
|
31
31
|
// Inherit our HKSV and motion awareness states from our parent camera.
|
|
32
32
|
this.accessory.context.detectMotion = savedContext.detectMotion ?? true;
|
|
33
|
-
this.
|
|
33
|
+
if (this.hasFeature("Video.HKSV.Recording.Switch")) {
|
|
34
|
+
// Compatibility with older releases. I'll remove this in the future.
|
|
35
|
+
if (savedContext.hksvRecording !== undefined) {
|
|
36
|
+
this.accessory.context.hksvRecordingDisabled = !savedContext.hksvRecording;
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
this.accessory.context.hksvRecordingDisabled = savedContext.hksvRecordingDisabled ?? false;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
34
42
|
// We explicitly avoid adding the MAC address of the camera - that's reserved for real Protect devices, not synthetic ones we create.
|
|
35
43
|
this.accessory.context.nvr = this.nvr.ufp.mac;
|
|
36
44
|
this.accessory.context.packageCamera = this.ufp.mac;
|
|
@@ -103,8 +111,8 @@ export class ProtectCameraPackage extends ProtectCamera {
|
|
|
103
111
|
return false;
|
|
104
112
|
}
|
|
105
113
|
// Activate or deactivate the package camera flashlight.
|
|
106
|
-
service.getCharacteristic(this.hap.Characteristic.On)
|
|
107
|
-
service.getCharacteristic(this.hap.Characteristic.On)
|
|
114
|
+
service.getCharacteristic(this.hap.Characteristic.On).onGet(() => !!this.flashlightState);
|
|
115
|
+
service.getCharacteristic(this.hap.Characteristic.On).onSet(async (value) => {
|
|
108
116
|
// Stop heartbeating the flashlight to allow it to turn off.
|
|
109
117
|
if (!value) {
|
|
110
118
|
clearInterval(this.flashlightTimer);
|
|
@@ -138,7 +146,7 @@ export class ProtectCameraPackage extends ProtectCamera {
|
|
|
138
146
|
clearInterval(this.flashlightTimer);
|
|
139
147
|
// If it's dark, we're done.
|
|
140
148
|
if (!this.ufp.isDark) {
|
|
141
|
-
setTimeout(() => service
|
|
149
|
+
setTimeout(() => service.updateCharacteristic(this.hap.Characteristic.On, false), 50);
|
|
142
150
|
return;
|
|
143
151
|
}
|
|
144
152
|
// Activate the flashlight.
|
|
@@ -164,8 +172,8 @@ export class ProtectCameraPackage extends ProtectCamera {
|
|
|
164
172
|
return {
|
|
165
173
|
channel: channel,
|
|
166
174
|
lens: 2,
|
|
167
|
-
name: this.getResolution([channel.width, channel.height, channel.fps]) + " (" + channel.name + ") [" +
|
|
168
|
-
"]",
|
|
175
|
+
name: this.getResolution([channel.width, channel.height, channel.fps]) + " (" + channel.name + ") [" +
|
|
176
|
+
(this.ufp.videoCodec.replace("h265", "hevc")).toUpperCase() + "]",
|
|
169
177
|
resolution: [channel.width, channel.height, channel.fps],
|
|
170
178
|
url: "rtsps://" + this.nvr.config.address + ":" + this.nvr.ufp.ports.rtsps.toString() + "/" + channel.rtspAlias + "?enableSrtp"
|
|
171
179
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"protect-camera-package.js","sourceRoot":"","sources":["../../src/devices/protect-camera-package.ts"],"names":[],"mappings":"AAKA,OAAO,EAAkB,KAAK,EAAC,MAAM,yBAAyB,CAAC;AAC/D,OAAO,EAAE,aAAa,EAAkB,MAAM,qBAAqB,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AAEhE,gKAAgK;AAChK,MAAM,OAAO,oBAAqB,SAAQ,aAAa;IAE7C,eAAe,CAAW;IAC1B,eAAe,CAAkB;IAEzC,gCAAgC;IACtB,eAAe;QAEvB,yBAAyB;QACzB,MAAM,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,aAAa,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAEzD,IAAI,CAAC,eAAe,GAAG,KAAK,CAAC;QAC7B,IAAI,CAAC,KAAK,CAAC,SAAS,GAAG,KAAK,CAAC;QAE7B,oCAAoC;QACpC,IAAG,YAAY,EAAE,CAAC;YAEhB,IAAI,CAAC,KAAK,CAAC,YAAY,GAAG,YAAY,CAAC,KAAK,CAAC,YAAY,CAAC;YAC1D,IAAI,CAAC,KAAK,CAAC,gBAAgB,GAAG,YAAY,CAAC,KAAK,CAAC,gBAAgB,CAAC;YAClE,IAAI,CAAC,KAAK,CAAC,mBAAmB,GAAG,YAAY,CAAC,KAAK,CAAC,mBAAmB,CAAC;YACxE,IAAI,CAAC,KAAK,CAAC,gBAAgB,GAAG,YAAY,CAAC,KAAK,CAAC,gBAAgB,CAAC;YAClE,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,YAAY,CAAC,KAAK,CAAC,OAAO,CAAC;YAChD,IAAI,CAAC,KAAK,CAAC,SAAS,GAAG,YAAY,CAAC,KAAK,CAAC,SAAS,CAAC;YACpD,IAAI,CAAC,KAAK,CAAC,gBAAgB,GAAG,YAAY,CAAC,KAAK,CAAC,gBAAgB,CAAC;YAClE,IAAI,CAAC,KAAK,CAAC,oBAAoB,GAAG,YAAY,CAAC,KAAK,CAAC,oBAAoB,CAAC;YAC1E,IAAI,CAAC,KAAK,CAAC,2BAA2B,GAAG,YAAY,CAAC,KAAK,CAAC,2BAA2B,CAAC;QAC1F,CAAC;QAED,wDAAwD;QACxD,MAAM,YAAY,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC;QAE5C,mEAAmE;QACnE,IAAI,CAAC,SAAS,CAAC,OAAO,GAAG,EAAE,CAAC;QAE5B,uEAAuE;QACvE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,YAAY,GAAG,YAAY,CAAC,
|
|
1
|
+
{"version":3,"file":"protect-camera-package.js","sourceRoot":"","sources":["../../src/devices/protect-camera-package.ts"],"names":[],"mappings":"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"}
|
|
@@ -29,11 +29,10 @@ export declare class ProtectCamera extends ProtectDevice {
|
|
|
29
29
|
detectLicensePlate: string[];
|
|
30
30
|
readonly livestream: LivestreamManager;
|
|
31
31
|
messageSwitches: {
|
|
32
|
-
[index: string]: MessageSwitchInterface;
|
|
32
|
+
[index: string]: MessageSwitchInterface | undefined;
|
|
33
33
|
};
|
|
34
34
|
packageCamera?: Nullable<ProtectCameraPackage>;
|
|
35
35
|
private rtspEntries;
|
|
36
|
-
private rtspQuality;
|
|
37
36
|
stream: ProtectStreamingDelegate;
|
|
38
37
|
ufp: ProtectCameraConfig;
|
|
39
38
|
constructor(nvr: ProtectNvr, device: ProtectCameraConfig, accessory: PlatformAccessory);
|
|
@@ -45,6 +44,7 @@ export declare class ProtectCamera extends ProtectDevice {
|
|
|
45
44
|
private configureAmbientLightSensor;
|
|
46
45
|
private configureAccessFeatures;
|
|
47
46
|
private configureMotionSmartSensor;
|
|
47
|
+
private configureDoorbellMuteSwitch;
|
|
48
48
|
private configureDoorbellTrigger;
|
|
49
49
|
protected configureVideoDoorbell(): boolean;
|
|
50
50
|
private configureCameraDetails;
|
|
@@ -64,5 +64,6 @@ export declare class ProtectCamera extends ProtectDevice {
|
|
|
64
64
|
get isHksvCapable(): boolean;
|
|
65
65
|
private get nightVision();
|
|
66
66
|
private get nightVisionBrightness();
|
|
67
|
+
get audioFilters(): string[];
|
|
67
68
|
}
|
|
68
69
|
export {};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { ProtectReservedNames, toCamelCase } from "../protect-types.js";
|
|
2
2
|
import { LivestreamManager } from "../protect-livestream.js";
|
|
3
|
+
import { PROTECT_FFMPEG_AUDIO_FILTER_FFTNR } 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 {
|
|
@@ -13,7 +14,6 @@ export class ProtectCamera extends ProtectDevice {
|
|
|
13
14
|
messageSwitches;
|
|
14
15
|
packageCamera;
|
|
15
16
|
rtspEntries;
|
|
16
|
-
rtspQuality;
|
|
17
17
|
stream;
|
|
18
18
|
ufp;
|
|
19
19
|
// Create an instance.
|
|
@@ -26,7 +26,6 @@ export class ProtectCamera extends ProtectDevice {
|
|
|
26
26
|
this.livestream = new LivestreamManager(this);
|
|
27
27
|
this.messageSwitches = {};
|
|
28
28
|
this.rtspEntries = [];
|
|
29
|
-
this.rtspQuality = {};
|
|
30
29
|
this.ufp = device;
|
|
31
30
|
this.configureHints();
|
|
32
31
|
this.configureDevice();
|
|
@@ -55,10 +54,10 @@ export class ProtectCamera extends ProtectDevice {
|
|
|
55
54
|
this.hints.twoWayAudio = this.ufp.featureFlags.hasSpeaker && this.hasFeature("Audio") && this.hasFeature("Audio.TwoWay");
|
|
56
55
|
this.hints.twoWayAudioDirect = this.ufp.featureFlags.hasSpeaker && this.hasFeature("Audio") && this.hasFeature("Audio.TwoWay.Direct");
|
|
57
56
|
// Sanity check our target transcoding bitrates, if defined.
|
|
58
|
-
if (
|
|
57
|
+
if (!this.hints.transcodeBitrate || (this.hints.transcodeBitrate <= 0)) {
|
|
59
58
|
this.hints.transcodeBitrate = -1;
|
|
60
59
|
}
|
|
61
|
-
if (
|
|
60
|
+
if (!this.hints.transcodeHighLatencyBitrate || (this.hints.transcodeHighLatencyBitrate <= 0)) {
|
|
62
61
|
this.hints.transcodeHighLatencyBitrate = -1;
|
|
63
62
|
}
|
|
64
63
|
return true;
|
|
@@ -70,9 +69,20 @@ export class ProtectCamera extends ProtectDevice {
|
|
|
70
69
|
// Clean out the context object in case it's been polluted somehow.
|
|
71
70
|
this.accessory.context = {};
|
|
72
71
|
this.accessory.context.detectMotion = savedContext.detectMotion ?? true;
|
|
73
|
-
this.accessory.context.hksvRecording = savedContext.hksvRecording ?? true;
|
|
74
72
|
this.accessory.context.mac = this.ufp.mac;
|
|
75
73
|
this.accessory.context.nvr = this.nvr.ufp.mac;
|
|
74
|
+
if (this.hasFeature("Video.HKSV.Recording.Switch")) {
|
|
75
|
+
// Compatibility with older releases. I'll remove this in the future.
|
|
76
|
+
if (savedContext.hksvRecording !== undefined) {
|
|
77
|
+
this.accessory.context.hksvRecordingDisabled = !savedContext.hksvRecording;
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
this.accessory.context.hksvRecordingDisabled = savedContext.hksvRecordingDisabled ?? false;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
if (this.hasFeature("Doorbell.Mute")) {
|
|
84
|
+
this.accessory.context.doorbellMuted = savedContext.doorbellMuted ?? false;
|
|
85
|
+
}
|
|
76
86
|
// Inform the user that motion detection will suck.
|
|
77
87
|
if (this.ufp.recordingSettings.mode === "never") {
|
|
78
88
|
this.log.warn("Motion events will not be generated by the Protect controller when the controller's camera recording options are set to \"never\".");
|
|
@@ -114,6 +124,8 @@ export class ProtectCamera extends ProtectDevice {
|
|
|
114
124
|
this.configureStatusLedSwitch();
|
|
115
125
|
// Configure the night vision indicator light switch.
|
|
116
126
|
this.configureNightVisionDimmer();
|
|
127
|
+
// Configure the doorbell mute switch.
|
|
128
|
+
this.configureDoorbellMuteSwitch();
|
|
117
129
|
// Configure the doorbell trigger.
|
|
118
130
|
this.configureDoorbellTrigger();
|
|
119
131
|
// Listen for events.
|
|
@@ -125,15 +137,13 @@ export class ProtectCamera extends ProtectDevice {
|
|
|
125
137
|
// Cleanup after ourselves if we're being deleted.
|
|
126
138
|
cleanup() {
|
|
127
139
|
// If we've got HomeKit Secure Video enabled and recording, disable it.
|
|
128
|
-
if (this.stream
|
|
140
|
+
if (this.stream.hksv?.isRecording) {
|
|
129
141
|
void this.stream.hksv.updateRecordingActive(false);
|
|
130
142
|
}
|
|
131
143
|
// Cleanup our livestream manager.
|
|
132
144
|
this.livestream.shutdown();
|
|
133
145
|
// Unregister our controller.
|
|
134
|
-
|
|
135
|
-
this.accessory.removeController(this.stream.controller);
|
|
136
|
-
}
|
|
146
|
+
this.accessory.removeController(this.stream.controller);
|
|
137
147
|
super.cleanup();
|
|
138
148
|
this.isDeleted = true;
|
|
139
149
|
}
|
|
@@ -152,7 +162,7 @@ export class ProtectCamera extends ProtectDevice {
|
|
|
152
162
|
// - HKSV recording enabled.
|
|
153
163
|
// - No enabled smart motion detection capabilities on the Protect device.
|
|
154
164
|
// - Smart detection disabled.
|
|
155
|
-
if (this.stream
|
|
165
|
+
if (this.stream.hksv?.isRecording ||
|
|
156
166
|
!(this.ufp.featureFlags.smartDetectAudioTypes.length || this.ufp.featureFlags.smartDetectTypes.length) || !this.hints.smartDetect) {
|
|
157
167
|
this.nvr.events.motionEventHandler(this);
|
|
158
168
|
}
|
|
@@ -164,8 +174,9 @@ export class ProtectCamera extends ProtectDevice {
|
|
|
164
174
|
// If smart detection is enabled, we need to filter out any events tagged as "motion". When users enable the "Create motion events" setting on a camera, Protect will
|
|
165
175
|
// create motion-specific thumbnail events. We're only interested in true smart detection events.
|
|
166
176
|
if (this.hints.smartDetect) {
|
|
167
|
-
|
|
168
|
-
|
|
177
|
+
const event = payload;
|
|
178
|
+
if (event.metadata?.detectedThumbnails) {
|
|
179
|
+
event.metadata.detectedThumbnails = event.metadata.detectedThumbnails.filter(({ type }) => type !== "motion");
|
|
169
180
|
}
|
|
170
181
|
}
|
|
171
182
|
// Process smart detection events that have occurred on a non-realtime basis. Generally, this includes audio and video events that require more analysis by Protect.
|
|
@@ -186,7 +197,7 @@ export class ProtectCamera extends ProtectDevice {
|
|
|
186
197
|
addEventHandler(packet) {
|
|
187
198
|
const payload = packet.payload;
|
|
188
199
|
// Detect UniFi Access unlock events surfaced in Protect.
|
|
189
|
-
if ((packet.header.modelKey === "event") && (payload.metadata?.action === "open_door") && payload.metadata
|
|
200
|
+
if ((packet.header.modelKey === "event") && (payload.metadata?.action === "open_door") && payload.metadata.openSuccess) {
|
|
190
201
|
const lockService = this.accessory.getServiceById(this.hap.Service.LockMechanism, ProtectReservedNames.LOCK_ACCESS);
|
|
191
202
|
if (!lockService) {
|
|
192
203
|
return;
|
|
@@ -198,8 +209,8 @@ export class ProtectCamera extends ProtectDevice {
|
|
|
198
209
|
clearTimeout(this.accessUnlockTimer);
|
|
199
210
|
}
|
|
200
211
|
this.accessUnlockTimer = setTimeout(() => {
|
|
201
|
-
lockService
|
|
202
|
-
lockService
|
|
212
|
+
lockService.updateCharacteristic(this.hap.Characteristic.LockTargetState, this.hap.Characteristic.LockTargetState.SECURED);
|
|
213
|
+
lockService.updateCharacteristic(this.hap.Characteristic.LockCurrentState, this.hap.Characteristic.LockCurrentState.SECURED);
|
|
203
214
|
this.accessUnlockTimer = undefined;
|
|
204
215
|
}, 2000);
|
|
205
216
|
return;
|
|
@@ -211,7 +222,7 @@ export class ProtectCamera extends ProtectDevice {
|
|
|
211
222
|
// - We explicitly filter out events tagged as "motion". When users enable the "Create motion events" setting on a camera, Protect will create motion-specific
|
|
212
223
|
// thumbnail events. We're only interested in true smart detection events.
|
|
213
224
|
if (!this.hints.smartDetect || !((packet.header.modelKey === "smartDetectObject") || ((packet.header.modelKey === "event") &&
|
|
214
|
-
["smartDetectLine", "smartDetectZone"].includes(payload.type) && (payload.type !== "motion") && payload.smartDetectTypes
|
|
225
|
+
["smartDetectLine", "smartDetectZone"].includes(payload.type) && (payload.type !== "motion") && payload.smartDetectTypes?.length))) {
|
|
215
226
|
return;
|
|
216
227
|
}
|
|
217
228
|
// Process the motion event.
|
|
@@ -241,11 +252,9 @@ export class ProtectCamera extends ProtectDevice {
|
|
|
241
252
|
return -1;
|
|
242
253
|
}
|
|
243
254
|
try {
|
|
244
|
-
let lux = (await response?.body.json()).illuminance ??
|
|
255
|
+
let lux = (await response?.body.json()).illuminance ?? 0;
|
|
245
256
|
// The minimum value for ambient light in HomeKit is 0.0001. I have no idea why...but it is. Honor it.
|
|
246
|
-
|
|
247
|
-
lux = 0.0001;
|
|
248
|
-
}
|
|
257
|
+
lux ||= 0.0001;
|
|
249
258
|
return lux;
|
|
250
259
|
// eslint-disable-next-line @stylistic/keyword-spacing
|
|
251
260
|
}
|
|
@@ -273,14 +282,14 @@ export class ProtectCamera extends ProtectDevice {
|
|
|
273
282
|
this.publish("ambientlight", this.ambientLight.toString());
|
|
274
283
|
}, 60 * 1000);
|
|
275
284
|
// Retrieve the active state when requested.
|
|
276
|
-
service.getCharacteristic(this.hap.Characteristic.StatusActive)
|
|
285
|
+
service.getCharacteristic(this.hap.Characteristic.StatusActive).onGet(() => this.isOnline);
|
|
277
286
|
// Initialize the sensor.
|
|
278
287
|
this.ambientLight = await getLux();
|
|
279
288
|
if (this.ambientLight === -1) {
|
|
280
289
|
this.ambientLight = 0.0001;
|
|
281
290
|
}
|
|
282
291
|
// Retrieve the current light level when requested.
|
|
283
|
-
service.getCharacteristic(this.hap.Characteristic.CurrentAmbientLightLevel)
|
|
292
|
+
service.getCharacteristic(this.hap.Characteristic.CurrentAmbientLightLevel).onGet(() => this.ambientLight);
|
|
284
293
|
service.updateCharacteristic(this.hap.Characteristic.CurrentAmbientLightLevel, this.ambientLight);
|
|
285
294
|
service.updateCharacteristic(this.hap.Characteristic.StatusActive, this.isOnline);
|
|
286
295
|
return true;
|
|
@@ -288,7 +297,7 @@ export class ProtectCamera extends ProtectDevice {
|
|
|
288
297
|
// Configure UniFi Access specific features for devices that are made available in Protect.
|
|
289
298
|
configureAccessFeatures() {
|
|
290
299
|
// If the Access device doesn't have unlock capabilities, we're done.
|
|
291
|
-
if (!this.ufp.accessDeviceMetadata?.featureFlags
|
|
300
|
+
if (!this.ufp.accessDeviceMetadata?.featureFlags.supportUnlock) {
|
|
292
301
|
return false;
|
|
293
302
|
}
|
|
294
303
|
// Validate whether we should have this service enabled.
|
|
@@ -308,8 +317,8 @@ export class ProtectCamera extends ProtectDevice {
|
|
|
308
317
|
if (value === this.hap.Characteristic.LockTargetState.SECURED) {
|
|
309
318
|
// Let's make sure we revert the lock to it's prior state.
|
|
310
319
|
setTimeout(() => {
|
|
311
|
-
service
|
|
312
|
-
service
|
|
320
|
+
service.updateCharacteristic(this.hap.Characteristic.LockTargetState, this.hap.Characteristic.LockTargetState.UNSECURED);
|
|
321
|
+
service.updateCharacteristic(this.hap.Characteristic.LockCurrentState, this.hap.Characteristic.LockCurrentState.UNSECURED);
|
|
313
322
|
}, 50);
|
|
314
323
|
return;
|
|
315
324
|
}
|
|
@@ -318,8 +327,8 @@ export class ProtectCamera extends ProtectDevice {
|
|
|
318
327
|
if (this.nvr.ufpApi.responseOk(response?.statusCode)) {
|
|
319
328
|
// Something went wrong, revert to our prior state.
|
|
320
329
|
setTimeout(() => {
|
|
321
|
-
service
|
|
322
|
-
service
|
|
330
|
+
service.updateCharacteristic(this.hap.Characteristic.LockTargetState, this.hap.Characteristic.LockTargetState.UNSECURED);
|
|
331
|
+
service.updateCharacteristic(this.hap.Characteristic.LockCurrentState, this.hap.Characteristic.LockCurrentState.UNSECURED);
|
|
323
332
|
}, 50);
|
|
324
333
|
return;
|
|
325
334
|
}
|
|
@@ -341,7 +350,7 @@ export class ProtectCamera extends ProtectDevice {
|
|
|
341
350
|
}
|
|
342
351
|
// We don't have this contact sensor enabled, remove it.
|
|
343
352
|
this.accessory.removeService(objectService);
|
|
344
|
-
this.log.info("Disabling smart motion license plate contact sensor: %s.", objectService.subtype?.slice(objectService.subtype
|
|
353
|
+
this.log.info("Disabling smart motion license plate contact sensor: %s.", objectService.subtype?.slice(objectService.subtype.indexOf(".") + 1));
|
|
345
354
|
}
|
|
346
355
|
// If we don't have smart motion detection available or we have smart motion object contact sensors disabled, let's remove them.
|
|
347
356
|
if (!this.hints.smartDetectSensors) {
|
|
@@ -349,7 +358,7 @@ export class ProtectCamera extends ProtectDevice {
|
|
|
349
358
|
for (const objectService of this.accessory.services.filter(x => x.subtype?.startsWith(ProtectReservedNames.CONTACT_MOTION_SMARTDETECT + "."))) {
|
|
350
359
|
// We don't have this contact sensor enabled, remove it.
|
|
351
360
|
this.accessory.removeService(objectService);
|
|
352
|
-
this.log.info("Disabling smart motion contact sensor: %s.", objectService.subtype?.slice(objectService.subtype
|
|
361
|
+
this.log.info("Disabling smart motion contact sensor: %s.", objectService.subtype?.slice(objectService.subtype.indexOf(".") + 1));
|
|
353
362
|
}
|
|
354
363
|
}
|
|
355
364
|
// If we don't have smart motion detection, we're done.
|
|
@@ -394,6 +403,32 @@ export class ProtectCamera extends ProtectDevice {
|
|
|
394
403
|
}
|
|
395
404
|
return true;
|
|
396
405
|
}
|
|
406
|
+
// Configure a switch to mute doorbell ring events in HomeKit.
|
|
407
|
+
configureDoorbellMuteSwitch() {
|
|
408
|
+
// Validate whether we should have this service enabled.
|
|
409
|
+
if (!this.validService(this.hap.Service.Switch, this.hasFeature("Doorbell.Mute"), ProtectReservedNames.SWITCH_DOORBELL_MUTE) ||
|
|
410
|
+
!this.accessory.getService(this.hap.Service.Doorbell)) {
|
|
411
|
+
delete this.accessory.context.doorbellMuted;
|
|
412
|
+
return false;
|
|
413
|
+
}
|
|
414
|
+
// Add the switch to the camera, if needed.
|
|
415
|
+
const service = this.acquireService(this.hap.Service.Switch, this.accessoryName + " Doorbell Mute", ProtectReservedNames.SWITCH_DOORBELL_MUTE);
|
|
416
|
+
// Fail gracefully.
|
|
417
|
+
if (!service) {
|
|
418
|
+
this.log.error("Unable to add the doorbell mute switch.");
|
|
419
|
+
return false;
|
|
420
|
+
}
|
|
421
|
+
// Configure the switch.
|
|
422
|
+
service.getCharacteristic(this.hap.Characteristic.On).onGet(() => this.accessory.context.doorbellMuted);
|
|
423
|
+
service.getCharacteristic(this.hap.Characteristic.On).onSet((value) => {
|
|
424
|
+
this.accessory.context.doorbellMuted = !!value;
|
|
425
|
+
this.log.info("Doorbell chime %s.", value ? "disabled" : "enabled");
|
|
426
|
+
});
|
|
427
|
+
// Initialize the switch.
|
|
428
|
+
service.updateCharacteristic(this.hap.Characteristic.On, this.accessory.context.doorbellMuted);
|
|
429
|
+
this.log.info("Enabling doorbell mute switch.");
|
|
430
|
+
return true;
|
|
431
|
+
}
|
|
397
432
|
// Configure a switch to manually trigger a doorbell ring event for HomeKit.
|
|
398
433
|
configureDoorbellTrigger() {
|
|
399
434
|
// See if we have a doorbell service configured.
|
|
@@ -426,10 +461,8 @@ export class ProtectCamera extends ProtectDevice {
|
|
|
426
461
|
return false;
|
|
427
462
|
}
|
|
428
463
|
// Trigger the doorbell.
|
|
429
|
-
triggerService.getCharacteristic(this.hap.Characteristic.On)
|
|
430
|
-
|
|
431
|
-
});
|
|
432
|
-
triggerService.getCharacteristic(this.hap.Characteristic.On)?.onSet((value) => {
|
|
464
|
+
triggerService.getCharacteristic(this.hap.Characteristic.On).onGet(() => this.isRinging);
|
|
465
|
+
triggerService.getCharacteristic(this.hap.Characteristic.On).onSet((value) => {
|
|
433
466
|
if (value) {
|
|
434
467
|
// Trigger the ring event.
|
|
435
468
|
this.nvr.events.doorbellEventHandler(this, Date.now());
|
|
@@ -438,7 +471,7 @@ export class ProtectCamera extends ProtectDevice {
|
|
|
438
471
|
else {
|
|
439
472
|
// If the doorbell ring event is still going, we should be as well.
|
|
440
473
|
if (this.isRinging) {
|
|
441
|
-
setTimeout(() => triggerService
|
|
474
|
+
setTimeout(() => triggerService.updateCharacteristic(this.hap.Characteristic.On, true), 50);
|
|
442
475
|
}
|
|
443
476
|
}
|
|
444
477
|
});
|
|
@@ -492,7 +525,7 @@ export class ProtectCamera extends ProtectDevice {
|
|
|
492
525
|
const newUfp = await this.nvr.ufpApi.updateDevice(this.ufp, { ispSettings: { irLedMode: value ? "auto" : "off" } });
|
|
493
526
|
if (!newUfp) {
|
|
494
527
|
this.log.error("Unable to set night vision to %s. Please ensure this username has the Administrator role in UniFi Protect.", value ? "auto" : "off");
|
|
495
|
-
setTimeout(() => service
|
|
528
|
+
setTimeout(() => service.updateCharacteristic(this.hap.Characteristic.NightVision, !value), 50);
|
|
496
529
|
return;
|
|
497
530
|
}
|
|
498
531
|
// Update our internal view of the device configuration.
|
|
@@ -542,7 +575,7 @@ export class ProtectCamera extends ProtectDevice {
|
|
|
542
575
|
async configureVideoStream() {
|
|
543
576
|
const rtspEntries = [];
|
|
544
577
|
// No channels exist on this camera or we don't have access to the bootstrap configuration.
|
|
545
|
-
if (!this.ufp.channels) {
|
|
578
|
+
if (!this.ufp.channels.length) {
|
|
546
579
|
return false;
|
|
547
580
|
}
|
|
548
581
|
// Enable RTSP on the camera if needed and get the list of RTSP streams we have ultimately configured.
|
|
@@ -550,7 +583,7 @@ export class ProtectCamera extends ProtectDevice {
|
|
|
550
583
|
// Figure out which camera channels are RTSP-enabled, and user-enabled. We also filter out any package camera entries. We deal with those independently elsewhere.
|
|
551
584
|
const cameraChannels = this.ufp.channels.filter(channel => channel.isRtspEnabled && (channel.name !== "Package Camera"));
|
|
552
585
|
// Set the camera and shapshot URLs.
|
|
553
|
-
const cameraUrl = "rtsps://" + (this.nvr.config.overrideAddress ?? this.ufp.connectionHost
|
|
586
|
+
const cameraUrl = "rtsps://" + (this.nvr.config.overrideAddress ?? this.ufp.connectionHost) + ":" + this.nvr.ufp.ports.rtsps.toString() + "/";
|
|
554
587
|
// No RTSP streams are available that meet our criteria - we're done.
|
|
555
588
|
if (!cameraChannels.length) {
|
|
556
589
|
this.log.info("No RTSP profiles found for this camera. " +
|
|
@@ -643,6 +676,7 @@ export class ProtectCamera extends ProtectDevice {
|
|
|
643
676
|
// Publish our updated list of supported resolutions and their URLs.
|
|
644
677
|
this.rtspEntries = rtspEntries;
|
|
645
678
|
// If we've already configured the HomeKit video streaming delegate, we're done here.
|
|
679
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
646
680
|
if (this.stream) {
|
|
647
681
|
return true;
|
|
648
682
|
}
|
|
@@ -714,8 +748,8 @@ export class ProtectCamera extends ProtectDevice {
|
|
|
714
748
|
configureHksvRecordingSwitch() {
|
|
715
749
|
// Validate whether we should have this service enabled.
|
|
716
750
|
if (!this.validService(this.hap.Service.Switch, this.hasFeature("Video.HKSV.Recording.Switch"), ProtectReservedNames.SWITCH_HKSV_RECORDING)) {
|
|
717
|
-
//
|
|
718
|
-
this.accessory.context.
|
|
751
|
+
// Remove our stateful context since it's unneeded.
|
|
752
|
+
delete this.accessory.context.hksvRecordingDisabled;
|
|
719
753
|
return false;
|
|
720
754
|
}
|
|
721
755
|
// Acquire the service.
|
|
@@ -726,17 +760,15 @@ export class ProtectCamera extends ProtectDevice {
|
|
|
726
760
|
return false;
|
|
727
761
|
}
|
|
728
762
|
// Activate or deactivate HKSV recording.
|
|
729
|
-
service.getCharacteristic(this.hap.Characteristic.On)
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
service.getCharacteristic(this.hap.Characteristic.On)?.onSet((value) => {
|
|
733
|
-
if (this.accessory.context.hksvRecording !== value) {
|
|
763
|
+
service.getCharacteristic(this.hap.Characteristic.On).onGet(() => !this.accessory.context.hksvRecordingDisabled);
|
|
764
|
+
service.getCharacteristic(this.hap.Characteristic.On).onSet((value) => {
|
|
765
|
+
if (this.accessory.context.hksvRecordingDisabled !== !value) {
|
|
734
766
|
this.log.info("HKSV event recording %s.", value ? "enabled" : "disabled");
|
|
735
767
|
}
|
|
736
|
-
this.accessory.context.
|
|
768
|
+
this.accessory.context.hksvRecordingDisabled = !value;
|
|
737
769
|
});
|
|
738
770
|
// Initialize the switch.
|
|
739
|
-
service.updateCharacteristic(this.hap.Characteristic.On, this.accessory.context.
|
|
771
|
+
service.updateCharacteristic(this.hap.Characteristic.On, !this.accessory.context.hksvRecordingDisabled);
|
|
740
772
|
this.log.info("Enabling HKSV recording switch.");
|
|
741
773
|
return true;
|
|
742
774
|
}
|
|
@@ -755,8 +787,8 @@ export class ProtectCamera extends ProtectDevice {
|
|
|
755
787
|
return false;
|
|
756
788
|
}
|
|
757
789
|
// Adjust night vision capabilities.
|
|
758
|
-
service.getCharacteristic(this.hap.Characteristic.On)
|
|
759
|
-
service.getCharacteristic(this.hap.Characteristic.On)
|
|
790
|
+
service.getCharacteristic(this.hap.Characteristic.On).onGet(() => this.nightVision);
|
|
791
|
+
service.getCharacteristic(this.hap.Characteristic.On).onSet(async (value) => {
|
|
760
792
|
if (this.nightVision !== value) {
|
|
761
793
|
this.log.info("Night vision %s.", value ? "enabled" : "disabled");
|
|
762
794
|
}
|
|
@@ -765,15 +797,15 @@ export class ProtectCamera extends ProtectDevice {
|
|
|
765
797
|
const newUfp = await this.nvr.ufpApi.updateDevice(this.ufp, { ispSettings: { irLedMode: value ? mode : "off" } });
|
|
766
798
|
if (!newUfp) {
|
|
767
799
|
this.log.error("Unable to set night vision to %s. Please ensure this username has the Administrator role in UniFi Protect.", value ? "custom" : "off");
|
|
768
|
-
setTimeout(() => service
|
|
800
|
+
setTimeout(() => service.updateCharacteristic(this.hap.Characteristic.On, !value), 50);
|
|
769
801
|
return;
|
|
770
802
|
}
|
|
771
803
|
// Update our internal view of the device configuration.
|
|
772
804
|
this.ufp = newUfp;
|
|
773
805
|
});
|
|
774
806
|
// Adjust the sensitivity of night vision.
|
|
775
|
-
service.getCharacteristic(this.hap.Characteristic.Brightness)
|
|
776
|
-
service.getCharacteristic(this.hap.Characteristic.Brightness)
|
|
807
|
+
service.getCharacteristic(this.hap.Characteristic.Brightness).onGet(() => this.nightVisionBrightness);
|
|
808
|
+
service.getCharacteristic(this.hap.Characteristic.Brightness).onSet(async (value) => {
|
|
777
809
|
let level = value;
|
|
778
810
|
let nightvision = {};
|
|
779
811
|
// If we're less than 10% in brightness, assume we want to disable night vision.
|
|
@@ -813,7 +845,7 @@ export class ProtectCamera extends ProtectDevice {
|
|
|
813
845
|
// Set the context to our updated device configuration.
|
|
814
846
|
this.ufp = newUfp;
|
|
815
847
|
// Make sure we properly reflect what brightness we're actually at, given the differences in setting granularity between Protect and HomeKit.
|
|
816
|
-
setTimeout(() => service
|
|
848
|
+
setTimeout(() => service.updateCharacteristic(this.hap.Characteristic.Brightness, level), 50);
|
|
817
849
|
});
|
|
818
850
|
// Initialize the dimmer state.
|
|
819
851
|
service.updateCharacteristic(this.hap.Characteristic.On, this.nightVision);
|
|
@@ -840,10 +872,8 @@ export class ProtectCamera extends ProtectDevice {
|
|
|
840
872
|
continue;
|
|
841
873
|
}
|
|
842
874
|
// Activate or deactivate the appropriate recording mode on the Protect controller.
|
|
843
|
-
service.getCharacteristic(this.hap.Characteristic.On)
|
|
844
|
-
|
|
845
|
-
});
|
|
846
|
-
service.getCharacteristic(this.hap.Characteristic.On)?.onSet(async (value) => {
|
|
875
|
+
service.getCharacteristic(this.hap.Characteristic.On).onGet(() => this.ufp.recordingSettings.mode === ufpRecordingSetting);
|
|
876
|
+
service.getCharacteristic(this.hap.Characteristic.On).onSet(async (value) => {
|
|
847
877
|
// 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
|
|
848
878
|
// settings one can choose from. Instead, we do nothing and leave it to the user to choose what state they really want to set.
|
|
849
879
|
if (!value) {
|
|
@@ -887,7 +917,7 @@ export class ProtectCamera extends ProtectDevice {
|
|
|
887
917
|
this.subscribeGet("rtsp", "RTSP information", () => {
|
|
888
918
|
// Grab all the available RTSP channels and return them as a JSON.
|
|
889
919
|
return JSON.stringify(Object.assign({}, ...this.ufp.channels.filter(channel => channel.isRtspEnabled)
|
|
890
|
-
.map(channel => ({ [channel.name]: "rtsps://" + (this.nvr.config.overrideAddress ?? this.ufp.connectionHost
|
|
920
|
+
.map(channel => ({ [channel.name]: "rtsps://" + (this.nvr.config.overrideAddress ?? this.ufp.connectionHost) + ":" +
|
|
891
921
|
this.nvr.ufp.ports.rtsp + "/" + channel.rtspAlias + "?enableSrtp" }))));
|
|
892
922
|
});
|
|
893
923
|
// Trigger snapshots when requested.
|
|
@@ -896,6 +926,7 @@ export class ProtectCamera extends ProtectDevice {
|
|
|
896
926
|
if (value !== "true") {
|
|
897
927
|
return;
|
|
898
928
|
}
|
|
929
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
899
930
|
void this.stream?.handleSnapshotRequest();
|
|
900
931
|
});
|
|
901
932
|
// Enable doorbell-specific MQTT capabilities only when we have a Protect doorbell or a doorbell trigger enabled.
|
|
@@ -951,7 +982,7 @@ export class ProtectCamera extends ProtectDevice {
|
|
|
951
982
|
findRtspEntry(width, height, options) {
|
|
952
983
|
const rtspEntries = options?.rtspEntries ?? this.rtspEntries;
|
|
953
984
|
// No RTSP entries to choose from, we're done.
|
|
954
|
-
if (!rtspEntries
|
|
985
|
+
if (!rtspEntries.length) {
|
|
955
986
|
return null;
|
|
956
987
|
}
|
|
957
988
|
// Second, we check to see if we've set an explicit preference for stream quality.
|
|
@@ -975,11 +1006,11 @@ export class ProtectCamera extends ProtectDevice {
|
|
|
975
1006
|
// Find a streaming RTSP configuration for a given target resolution.
|
|
976
1007
|
findRtsp(width, height, options) {
|
|
977
1008
|
// Create our options JSON if needed.
|
|
978
|
-
options
|
|
1009
|
+
options ??= {};
|
|
979
1010
|
// Set our default stream, if we've configured one.
|
|
980
1011
|
options.default = this.hints.streamingDefault;
|
|
981
1012
|
// See if we've been given RTSP entries or whether we should default to our own.
|
|
982
|
-
options.rtspEntries
|
|
1013
|
+
options.rtspEntries ??= this.rtspEntries;
|
|
983
1014
|
// If we've imposed a constraint on the maximum dimensions of what we want due to a hardware limitation, filter out those entries.
|
|
984
1015
|
if (options.maxPixels !== undefined) {
|
|
985
1016
|
options.rtspEntries = options.rtspEntries.filter(x => (x.channel.width * x.channel.height) <= (options.maxPixels ?? Infinity));
|
|
@@ -1025,7 +1056,7 @@ export class ProtectCamera extends ProtectDevice {
|
|
|
1025
1056
|
}
|
|
1026
1057
|
// Utility property to return the current night vision state of a camera. It's a blunt instrument due to HomeKit constraints.
|
|
1027
1058
|
get nightVision() {
|
|
1028
|
-
return this.ufp
|
|
1059
|
+
return this.ufp.ispSettings.irLedMode !== "off";
|
|
1029
1060
|
}
|
|
1030
1061
|
// Utility property to return the current night vision state of a camera, mapped to a brightness characteristic.
|
|
1031
1062
|
get nightVisionBrightness() {
|
|
@@ -1044,5 +1075,34 @@ export class ProtectCamera extends ProtectDevice {
|
|
|
1044
1075
|
return 0;
|
|
1045
1076
|
}
|
|
1046
1077
|
}
|
|
1078
|
+
// Utility to return our audio filter pipeline for this camera.
|
|
1079
|
+
get audioFilters() {
|
|
1080
|
+
// If we don't have audio filtering enabled, we're done.
|
|
1081
|
+
if (!this.hasFeature("Audio.Filter.Noise")) {
|
|
1082
|
+
return [];
|
|
1083
|
+
}
|
|
1084
|
+
const afOptions = [];
|
|
1085
|
+
// See what the user has set for the afftdn filter for this camera.
|
|
1086
|
+
let fftNr = this.getFeatureFloat("Audio.Filter.Noise.FftNr") ?? PROTECT_FFMPEG_AUDIO_FILTER_FFTNR;
|
|
1087
|
+
// If we have an invalid setting, use the defaults.
|
|
1088
|
+
fftNr = Math.max(0.01, Math.min(97, fftNr));
|
|
1089
|
+
const highpass = this.getFeatureNumber("Audio.Filter.Noise.HighPass");
|
|
1090
|
+
const lowpass = this.getFeatureNumber("Audio.Filter.Noise.LowPass");
|
|
1091
|
+
// We use the following order of operations for our filter: highpass, then lowpass, and finally afftdn.
|
|
1092
|
+
// Only set the highpass and lowpass filters if the user has explicitly enabled them.
|
|
1093
|
+
if (typeof highpass === "number") {
|
|
1094
|
+
afOptions.push("highpass=p=2:f=" + highpass.toString());
|
|
1095
|
+
}
|
|
1096
|
+
if (typeof lowpass === "number") {
|
|
1097
|
+
afOptions.push("lowpass=p=2:f=" + lowpass.toString());
|
|
1098
|
+
}
|
|
1099
|
+
// The afftdn filter options we use are:
|
|
1100
|
+
//
|
|
1101
|
+
// nt=c Use the custom noise profile that we've measured for two seconds at the beginning of our session.
|
|
1102
|
+
// tn=1 Enable noise tracking.
|
|
1103
|
+
// nr=X Noise reduction value in decibels.
|
|
1104
|
+
afOptions.push("asendcmd=c='1.0 afftdn sn start ; 3.0 afftdn sn stop', afftdn=nt=c:tn=1:nr=" + fftNr.toString());
|
|
1105
|
+
return afOptions;
|
|
1106
|
+
}
|
|
1047
1107
|
}
|
|
1048
1108
|
//# sourceMappingURL=protect-camera.js.map
|