homebridge-unifi-protect 5.5.2 → 6.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/config.schema.json +17 -16
- package/dist/index.d.ts +3 -0
- package/dist/index.js +6 -6
- package/dist/index.js.map +1 -1
- package/dist/protect-camera.d.ts +58 -0
- package/dist/protect-camera.js +367 -246
- package/dist/protect-camera.js.map +1 -1
- package/dist/protect-device.d.ts +48 -0
- package/dist/protect-device.js +189 -0
- package/dist/protect-device.js.map +1 -0
- package/dist/protect-doorbell.d.ts +22 -0
- package/dist/protect-doorbell.js +75 -64
- package/dist/protect-doorbell.js.map +1 -1
- package/dist/protect-ffmpeg-record.d.ts +15 -0
- package/dist/protect-ffmpeg-record.js +48 -34
- package/dist/protect-ffmpeg-record.js.map +1 -1
- package/dist/protect-ffmpeg-stream.d.ts +15 -0
- package/dist/protect-ffmpeg-stream.js +22 -12
- package/dist/protect-ffmpeg-stream.js.map +1 -1
- package/dist/protect-ffmpeg.d.ts +42 -0
- package/dist/protect-ffmpeg.js +49 -58
- package/dist/protect-ffmpeg.js.map +1 -1
- package/dist/protect-light.d.ts +13 -0
- package/dist/protect-light.js +63 -40
- package/dist/protect-light.js.map +1 -1
- package/dist/protect-liveviews.d.ts +17 -0
- package/dist/protect-liveviews.js +117 -101
- package/dist/protect-liveviews.js.map +1 -1
- package/dist/protect-mqtt.d.ts +19 -0
- package/dist/protect-mqtt.js +26 -35
- package/dist/protect-mqtt.js.map +1 -1
- package/dist/protect-nvr-events.d.ts +30 -0
- package/dist/protect-nvr-events.js +168 -431
- package/dist/protect-nvr-events.js.map +1 -1
- package/dist/protect-nvr-systeminfo.d.ts +15 -0
- package/dist/protect-nvr-systeminfo.js +43 -49
- package/dist/protect-nvr-systeminfo.js.map +1 -1
- package/dist/protect-nvr.d.ts +48 -0
- package/dist/protect-nvr.js +327 -359
- package/dist/protect-nvr.js.map +1 -1
- package/dist/protect-options.d.ts +39 -0
- package/dist/protect-options.js +172 -6
- package/dist/protect-options.js.map +1 -1
- package/dist/protect-platform.d.ts +17 -0
- package/dist/protect-platform.js +17 -30
- package/dist/protect-platform.js.map +1 -1
- package/dist/protect-record.d.ts +33 -0
- package/dist/protect-record.js +130 -126
- package/dist/protect-record.js.map +1 -1
- package/dist/protect-rtp.d.ts +29 -0
- package/dist/protect-rtp.js +133 -16
- package/dist/protect-rtp.js.map +1 -1
- package/dist/protect-securitysystem.d.ts +18 -0
- package/dist/protect-securitysystem.js +105 -109
- package/dist/protect-securitysystem.js.map +1 -1
- package/dist/protect-sensor.d.ts +28 -0
- package/dist/protect-sensor.js +79 -97
- package/dist/protect-sensor.js.map +1 -1
- package/dist/protect-stream.d.ts +41 -0
- package/dist/protect-stream.js +298 -156
- package/dist/protect-stream.js.map +1 -1
- package/dist/protect-timeshift.d.ts +30 -0
- package/dist/protect-timeshift.js +65 -48
- package/dist/protect-timeshift.js.map +1 -1
- package/dist/protect-types.d.ts +50 -0
- package/dist/protect-types.js +22 -0
- package/dist/protect-types.js.map +1 -0
- package/dist/protect-viewer.d.ts +17 -0
- package/dist/protect-viewer.js +41 -47
- package/dist/protect-viewer.js.map +1 -1
- package/dist/settings.d.ts +22 -0
- package/dist/settings.js +30 -35
- package/dist/settings.js.map +1 -1
- package/homebridge-ui/public/index.html +715 -0
- package/homebridge-ui/server.js +156 -0
- package/package.json +15 -16
- package/dist/protect-accessory.js +0 -184
- package/dist/protect-accessory.js.map +0 -1
|
@@ -1,15 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.FfmpegStreamingProcess = void 0;
|
|
4
|
-
/* Copyright(C) 2017-2022, HJD (https://github.com/hjdhjd). All rights reserved.
|
|
1
|
+
/* Copyright(C) 2017-2023, HJD (https://github.com/hjdhjd). All rights reserved.
|
|
5
2
|
*
|
|
6
3
|
* protect-ffmpeg-stream.ts: Provide FFmpeg process control to support HomeKit livestreaming.
|
|
7
4
|
*
|
|
8
5
|
*/
|
|
9
|
-
|
|
10
|
-
|
|
6
|
+
import { FfmpegProcess } from "./protect-ffmpeg.js";
|
|
7
|
+
import { createSocket } from "node:dgram";
|
|
11
8
|
// FFmpeg streaming process management.
|
|
12
|
-
class FfmpegStreamingProcess extends
|
|
9
|
+
export class FfmpegStreamingProcess extends FfmpegProcess {
|
|
13
10
|
// Create a new FFmpeg process instance.
|
|
14
11
|
constructor(delegate, sessionId, commandLineArgs, returnPort, callback) {
|
|
15
12
|
// Initialize our parent.
|
|
@@ -21,6 +18,7 @@ class FfmpegStreamingProcess extends protect_ffmpeg_1.FfmpegProcess {
|
|
|
21
18
|
if (returnPort) {
|
|
22
19
|
this.createSocket(returnPort);
|
|
23
20
|
}
|
|
21
|
+
// Start it up, with appropriate error handling.
|
|
24
22
|
this.start(commandLineArgs, callback, async (errorMessage) => {
|
|
25
23
|
// Stop the stream.
|
|
26
24
|
await this.delegate.stopStream(this.sessionId);
|
|
@@ -39,7 +37,7 @@ class FfmpegStreamingProcess extends protect_ffmpeg_1.FfmpegProcess {
|
|
|
39
37
|
createSocket(portInfo) {
|
|
40
38
|
let errorListener;
|
|
41
39
|
let messageListener;
|
|
42
|
-
const socket =
|
|
40
|
+
const socket = createSocket(portInfo.addressVersion === "ipv6" ? "udp6" : "udp4");
|
|
43
41
|
// Cleanup after ourselves when the socket closes.
|
|
44
42
|
socket.once("close", () => {
|
|
45
43
|
if (this.streamTimeout) {
|
|
@@ -50,7 +48,7 @@ class FfmpegStreamingProcess extends protect_ffmpeg_1.FfmpegProcess {
|
|
|
50
48
|
});
|
|
51
49
|
// Handle potential network errors.
|
|
52
50
|
socket.on("error", errorListener = (error) => {
|
|
53
|
-
this.log.error("
|
|
51
|
+
this.log.error("Socket error: %s.", error.name);
|
|
54
52
|
void this.delegate.stopStream(this.sessionId);
|
|
55
53
|
});
|
|
56
54
|
// Manage our video streams in case we haven't received a stop request, but we're in fact dead zombies.
|
|
@@ -61,18 +59,30 @@ class FfmpegStreamingProcess extends protect_ffmpeg_1.FfmpegProcess {
|
|
|
61
59
|
}
|
|
62
60
|
// Set our new canary.
|
|
63
61
|
this.streamTimeout = setTimeout(() => {
|
|
64
|
-
this.debug("
|
|
62
|
+
this.log.debug("Video stream appears to be inactive for 5 seconds. Stopping stream.", this.protectCamera.name);
|
|
65
63
|
this.delegate.controller.forceStopStreamingSession(this.sessionId);
|
|
66
64
|
void this.delegate.stopStream(this.sessionId);
|
|
67
65
|
}, 5000);
|
|
68
66
|
});
|
|
69
67
|
// Bind to the port we're opening.
|
|
70
|
-
socket.bind(portInfo.port);
|
|
68
|
+
socket.bind(portInfo.port, (portInfo.addressVersion === "ipv6") ? "::1" : "127.0.0.1");
|
|
71
69
|
}
|
|
72
70
|
// Return the actual FFmpeg process.
|
|
73
71
|
get ffmpegProcess() {
|
|
74
72
|
return this.process;
|
|
75
73
|
}
|
|
74
|
+
// Log errors.
|
|
75
|
+
logFfmpegError(exitCode, signal) {
|
|
76
|
+
// Known streaming-related errors due to the performance and latency tweaks we've made to the FFmpeg command line.
|
|
77
|
+
const ffmpegKnownStreamingError = new RegExp("not enough frames to estimate rate; consider increasing probesize");
|
|
78
|
+
// See if we know about this error.
|
|
79
|
+
if (this.stderrLog.some(x => ffmpegKnownStreamingError.test(x))) {
|
|
80
|
+
// Let the streaming delegate know to adjust it's parameters for the next run and inform the user.
|
|
81
|
+
this.delegate.adjustProbeSize();
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
// Otherwise, revert to our default logging in our parent.
|
|
85
|
+
super.logFfmpegError(exitCode, signal);
|
|
86
|
+
}
|
|
76
87
|
}
|
|
77
|
-
exports.FfmpegStreamingProcess = FfmpegStreamingProcess;
|
|
78
88
|
//# sourceMappingURL=protect-ffmpeg-stream.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"protect-ffmpeg-stream.js","sourceRoot":"","sources":["../src/protect-ffmpeg-stream.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"protect-ffmpeg-stream.js","sourceRoot":"","sources":["../src/protect-ffmpeg-stream.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
/// <reference types="node" />
|
|
3
|
+
/// <reference types="node" />
|
|
4
|
+
/// <reference types="node" />
|
|
5
|
+
import { ChildProcessWithoutNullStreams } from "node:child_process";
|
|
6
|
+
import { ProtectCamera, ProtectPackageCamera } from "./protect-camera.js";
|
|
7
|
+
import { Readable, Writable } from "node:stream";
|
|
8
|
+
import { EventEmitter } from "node:events";
|
|
9
|
+
import { ProtectLogging } from "./protect-types.js";
|
|
10
|
+
import { ProtectNvr } from "./protect-nvr.js";
|
|
11
|
+
import { StreamRequestCallback } from "homebridge";
|
|
12
|
+
export interface PortInterface {
|
|
13
|
+
addressVersion: string;
|
|
14
|
+
port: number;
|
|
15
|
+
}
|
|
16
|
+
export declare class FfmpegProcess extends EventEmitter {
|
|
17
|
+
protected callback: StreamRequestCallback | null;
|
|
18
|
+
protected commandLineArgs: string[];
|
|
19
|
+
hasError: boolean;
|
|
20
|
+
isEnded: boolean;
|
|
21
|
+
private isLogging;
|
|
22
|
+
private isPrepared;
|
|
23
|
+
isStarted: boolean;
|
|
24
|
+
protected isVerbose: boolean;
|
|
25
|
+
private ffmpegTimeout?;
|
|
26
|
+
protected readonly log: ProtectLogging;
|
|
27
|
+
protected readonly nvr: ProtectNvr;
|
|
28
|
+
protected process: ChildProcessWithoutNullStreams | null;
|
|
29
|
+
protected protectCamera: ProtectCamera | ProtectPackageCamera;
|
|
30
|
+
protected stderrLog: string[];
|
|
31
|
+
constructor(protectCamera: ProtectCamera | ProtectPackageCamera, commandLineArgs?: string[], callback?: StreamRequestCallback);
|
|
32
|
+
protected prepareProcess(commandLineArgs?: string[], callback?: StreamRequestCallback): void;
|
|
33
|
+
protected start(commandLineArgs?: string[], callback?: StreamRequestCallback, errorHandler?: (errorMessage: string) => Promise<void>): void;
|
|
34
|
+
protected configureProcess(errorHandler?: (errorMessage: string) => Promise<void>): void;
|
|
35
|
+
protected stopProcess(): void;
|
|
36
|
+
stop(): void;
|
|
37
|
+
protected logFfmpegError(exitCode: number, signal: NodeJS.Signals): void;
|
|
38
|
+
get stdin(): Writable | null;
|
|
39
|
+
get stdout(): Readable | null;
|
|
40
|
+
get stderr(): Readable | null;
|
|
41
|
+
static codecEnabled(videoProcessor: string, codec: string, log: ProtectLogging): Promise<boolean>;
|
|
42
|
+
}
|
package/dist/protect-ffmpeg.js
CHANGED
|
@@ -1,38 +1,30 @@
|
|
|
1
|
-
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.FfmpegProcess = void 0;
|
|
7
|
-
/* Copyright(C) 2017-2022, HJD (https://github.com/hjdhjd). All rights reserved.
|
|
1
|
+
/* Copyright(C) 2017-2023, HJD (https://github.com/hjdhjd). All rights reserved.
|
|
8
2
|
*
|
|
9
3
|
* protect-ffmpeg.ts: Base class to provide FFmpeg process control and capability introspection.
|
|
10
4
|
*
|
|
11
|
-
* This module is heavily inspired by the homebridge and homebridge-camera-ffmpeg source code
|
|
12
|
-
* borrows heavily from both. Thank you for your contributions to the HomeKit world.
|
|
5
|
+
* This module is heavily inspired by the homebridge and homebridge-camera-ffmpeg source code. Thank you for your contributions to the HomeKit world.
|
|
13
6
|
*/
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
7
|
+
import { execFile, spawn } from "node:child_process";
|
|
8
|
+
import { EventEmitter } from "node:events";
|
|
9
|
+
import util from "node:util";
|
|
17
10
|
// Base class for all FFmpeg process management.
|
|
18
|
-
class FfmpegProcess extends
|
|
11
|
+
export class FfmpegProcess extends EventEmitter {
|
|
19
12
|
// Create a new FFmpeg process instance.
|
|
20
13
|
constructor(protectCamera, commandLineArgs, callback) {
|
|
21
14
|
// Initialize our parent.
|
|
22
15
|
super();
|
|
23
16
|
this.callback = null;
|
|
24
17
|
this.commandLineArgs = [];
|
|
25
|
-
this.
|
|
26
|
-
this.stderrLog = [];
|
|
18
|
+
this.hasError = false;
|
|
27
19
|
this.isLogging = false;
|
|
28
20
|
this.isPrepared = false;
|
|
29
21
|
this.isEnded = false;
|
|
30
22
|
this.isStarted = false;
|
|
31
|
-
this.log = protectCamera.
|
|
32
|
-
this.name = protectCamera.name.bind(protectCamera);
|
|
23
|
+
this.log = protectCamera.log;
|
|
33
24
|
this.nvr = protectCamera.nvr;
|
|
34
25
|
this.process = null;
|
|
35
26
|
this.protectCamera = protectCamera;
|
|
27
|
+
this.stderrLog = [];
|
|
36
28
|
// Toggle FFmpeg logging, if configured.
|
|
37
29
|
this.isVerbose = protectCamera.platform.verboseFfmpeg || protectCamera.stream.verboseFfmpeg;
|
|
38
30
|
// If we've specified a command line or a callback, let's save them.
|
|
@@ -51,7 +43,7 @@ class FfmpegProcess extends events_1.EventEmitter {
|
|
|
51
43
|
}
|
|
52
44
|
// No command line arguments - we're done.
|
|
53
45
|
if (!this.commandLineArgs) {
|
|
54
|
-
this.log.error("
|
|
46
|
+
this.log.error("No FFmpeg command line specified.");
|
|
55
47
|
return;
|
|
56
48
|
}
|
|
57
49
|
// Save the callback, if we have one.
|
|
@@ -69,10 +61,10 @@ class FfmpegProcess extends events_1.EventEmitter {
|
|
|
69
61
|
}
|
|
70
62
|
// Inform the user, if we've been asked to do so.
|
|
71
63
|
if (this.isLogging || this.isVerbose || this.protectCamera.platform.config.debugAll) {
|
|
72
|
-
this.log.info("
|
|
64
|
+
this.log.info("FFmpeg command: %s %s", this.protectCamera.stream.videoProcessor, this.commandLineArgs.join(" "));
|
|
73
65
|
}
|
|
74
66
|
else {
|
|
75
|
-
this.debug("
|
|
67
|
+
this.log.debug("FFmpeg command: %s %s", this.protectCamera.stream.videoProcessor, this.commandLineArgs.join(" "));
|
|
76
68
|
}
|
|
77
69
|
this.isPrepared = true;
|
|
78
70
|
}
|
|
@@ -82,43 +74,42 @@ class FfmpegProcess extends events_1.EventEmitter {
|
|
|
82
74
|
if (!this.isPrepared) {
|
|
83
75
|
this.prepareProcess(commandLineArgs, callback);
|
|
84
76
|
if (!this.isPrepared) {
|
|
85
|
-
this.log.error("
|
|
77
|
+
this.log.error("Error preparing to run FFmpeg.");
|
|
86
78
|
return;
|
|
87
79
|
}
|
|
88
80
|
}
|
|
89
81
|
// Execute the command line based on what we've prepared.
|
|
90
|
-
this.process =
|
|
82
|
+
this.process = spawn(this.protectCamera.stream.videoProcessor, this.commandLineArgs);
|
|
91
83
|
// Configure any post-spawn listeners and other plumbing.
|
|
92
84
|
this.configureProcess(errorHandler);
|
|
93
85
|
}
|
|
94
86
|
// Configure our FFmpeg process, once started.
|
|
95
87
|
configureProcess(errorHandler) {
|
|
96
|
-
var _a, _b, _c, _d, _e, _f;
|
|
97
88
|
let dataListener;
|
|
98
89
|
let errorListener;
|
|
99
90
|
// Handle errors emitted during process creation, such as an invalid command line.
|
|
100
|
-
|
|
101
|
-
this.log.error("
|
|
91
|
+
this.process?.once("error", (error) => {
|
|
92
|
+
this.log.error("FFmpeg failed to start: %s.", error.message);
|
|
102
93
|
// Execute our error handler, if one is provided.
|
|
103
94
|
if (errorHandler) {
|
|
104
95
|
void errorHandler(error.name + ": " + error.message);
|
|
105
96
|
}
|
|
106
97
|
});
|
|
107
98
|
// Handle errors on stdin.
|
|
108
|
-
|
|
99
|
+
this.process?.stdin?.on("error", errorListener = (error) => {
|
|
109
100
|
if (!error.message.includes("EPIPE")) {
|
|
110
|
-
this.log.error("
|
|
101
|
+
this.log.error("FFmpeg error: %s.", error.message);
|
|
111
102
|
}
|
|
112
103
|
});
|
|
113
104
|
// Handle logging output that gets sent to stderr.
|
|
114
|
-
|
|
105
|
+
this.process?.stderr?.on("data", dataListener = (data) => {
|
|
115
106
|
// Inform us when we start receiving data back from FFmpeg. We do this here because it's the only
|
|
116
107
|
// truly reliable place we can check on FFmpeg. stdin and stdout may not be used at all, depending
|
|
117
108
|
// on the way FFmpeg is called, but stderr will always be there.
|
|
118
109
|
if (!this.isStarted) {
|
|
119
110
|
this.isStarted = true;
|
|
120
111
|
this.isEnded = false;
|
|
121
|
-
this.debug("
|
|
112
|
+
this.log.debug("Received the first frame.");
|
|
122
113
|
this.emit("ffmpegStarted");
|
|
123
114
|
// Always remember to execute the callback once we're setup to let homebridge know we're streaming.
|
|
124
115
|
if (this.callback) {
|
|
@@ -135,14 +126,13 @@ class FfmpegProcess extends events_1.EventEmitter {
|
|
|
135
126
|
this.stderrLog.push(cleanLine + "\n");
|
|
136
127
|
// Show it to the user if it's been requested.
|
|
137
128
|
if (this.isLogging || this.isVerbose || this.protectCamera.platform.config.debugAll) {
|
|
138
|
-
this.log.info(
|
|
129
|
+
this.log.info(cleanLine);
|
|
139
130
|
}
|
|
140
131
|
}
|
|
141
132
|
}
|
|
142
133
|
});
|
|
143
134
|
// Handle our process termination.
|
|
144
|
-
|
|
145
|
-
var _a, _b, _c, _d, _e;
|
|
135
|
+
this.process?.once("exit", (exitCode, signal) => {
|
|
146
136
|
// Clear out our canary.
|
|
147
137
|
if (this.ffmpegTimeout) {
|
|
148
138
|
clearTimeout(this.ffmpegTimeout);
|
|
@@ -150,74 +140,76 @@ class FfmpegProcess extends events_1.EventEmitter {
|
|
|
150
140
|
this.isStarted = false;
|
|
151
141
|
this.isEnded = true;
|
|
152
142
|
// Some utilities to streamline things.
|
|
153
|
-
const logPrefix =
|
|
143
|
+
const logPrefix = "FFmpeg process ended ";
|
|
154
144
|
// FFmpeg ended normally and our canary didn't need to enforce FFmpeg's extinction.
|
|
155
145
|
if (this.ffmpegTimeout && exitCode === 0) {
|
|
156
|
-
this.debug(logPrefix + "(Normal).");
|
|
146
|
+
this.log.debug(logPrefix + "(Normal).");
|
|
157
147
|
}
|
|
158
|
-
else if (((exitCode === null) || (exitCode === 255)) &&
|
|
148
|
+
else if (((exitCode === null) || (exitCode === 255)) && this.process?.killed) {
|
|
159
149
|
// FFmpeg has ended. Let's figure out if it's because we killed it or whether it died of natural causes.
|
|
160
|
-
this.debug(logPrefix + (signal === "SIGKILL" ? "(Killed)." : "(Expected)."));
|
|
150
|
+
this.log.debug(logPrefix + (signal === "SIGKILL" ? "(Killed)." : "(Expected)."));
|
|
161
151
|
}
|
|
162
152
|
else {
|
|
163
|
-
//
|
|
164
|
-
this.
|
|
165
|
-
|
|
166
|
-
this.
|
|
153
|
+
// Flag that we've run into an FFmpeg error.
|
|
154
|
+
this.hasError = true;
|
|
155
|
+
// Inform the user.
|
|
156
|
+
this.logFfmpegError(exitCode, signal);
|
|
167
157
|
// Execute our error handler, if one is provided.
|
|
168
158
|
if (errorHandler) {
|
|
169
|
-
void errorHandler(
|
|
159
|
+
void errorHandler(util.format(this.protectCamera.name + ": " + logPrefix + " unexpectedly with exit code %s and signal %s.", exitCode, signal));
|
|
170
160
|
}
|
|
171
161
|
}
|
|
172
162
|
// Cleanup after ourselves.
|
|
173
|
-
|
|
174
|
-
|
|
163
|
+
this.process?.stdin?.removeListener("error", errorListener);
|
|
164
|
+
this.process?.stderr?.removeListener("data", dataListener);
|
|
175
165
|
this.process = null;
|
|
176
166
|
this.stderrLog = [];
|
|
177
167
|
});
|
|
178
168
|
}
|
|
179
169
|
// Stop the FFmpeg process and complete any cleanup activities.
|
|
180
170
|
stopProcess() {
|
|
181
|
-
var _a, _b, _c, _d;
|
|
182
171
|
// Check to make sure we aren't using stdin for data before telling FFmpeg we're done.
|
|
183
172
|
if (!this.commandLineArgs.includes("pipe:0")) {
|
|
184
|
-
|
|
173
|
+
this.process?.stdin.end("q");
|
|
185
174
|
}
|
|
186
175
|
// Close our input and output.
|
|
187
|
-
|
|
188
|
-
|
|
176
|
+
this.process?.stdin.destroy();
|
|
177
|
+
this.process?.stdout.destroy();
|
|
189
178
|
// In case we need to kill it again, just to be sure it's really dead.
|
|
190
179
|
this.ffmpegTimeout = setTimeout(() => {
|
|
191
|
-
|
|
192
|
-
(_a = this.process) === null || _a === void 0 ? void 0 : _a.kill("SIGKILL");
|
|
180
|
+
this.process?.kill("SIGKILL");
|
|
193
181
|
}, 5000);
|
|
194
182
|
// Send the kill shot.
|
|
195
|
-
|
|
183
|
+
this.process?.kill();
|
|
196
184
|
}
|
|
197
185
|
// Cleanup after we're done.
|
|
198
186
|
stop() {
|
|
199
187
|
this.stopProcess();
|
|
200
188
|
}
|
|
189
|
+
// Inform the user if an FFmpeg error occurs.
|
|
190
|
+
logFfmpegError(exitCode, signal) {
|
|
191
|
+
// Something else has occurred. Inform the user, and stop everything.
|
|
192
|
+
this.log.error("FFmpeg process ended unexpectedly with %s%s%s.", (exitCode !== null) ? "an exit code of " + exitCode.toString() : "", ((exitCode !== null) && signal) ? " and " : "", signal ? "a signal received of " + signal : "");
|
|
193
|
+
this.log.error("FFmpeg command line that errored out was: %s %s", this.protectCamera.stream.videoProcessor, this.commandLineArgs.join(" "));
|
|
194
|
+
this.stderrLog.map(x => this.log.error(x));
|
|
195
|
+
}
|
|
201
196
|
// Return the standard input for this process.
|
|
202
197
|
get stdin() {
|
|
203
|
-
|
|
204
|
-
return (_b = (_a = this.process) === null || _a === void 0 ? void 0 : _a.stdin) !== null && _b !== void 0 ? _b : null;
|
|
198
|
+
return this.process?.stdin ?? null;
|
|
205
199
|
}
|
|
206
200
|
// Return the standard output for this process.
|
|
207
201
|
get stdout() {
|
|
208
|
-
|
|
209
|
-
return (_b = (_a = this.process) === null || _a === void 0 ? void 0 : _a.stdout) !== null && _b !== void 0 ? _b : null;
|
|
202
|
+
return this.process?.stdout ?? null;
|
|
210
203
|
}
|
|
211
204
|
// Return the standard error for this process.
|
|
212
205
|
get stderr() {
|
|
213
|
-
|
|
214
|
-
return (_b = (_a = this.process) === null || _a === void 0 ? void 0 : _a.stderr) !== null && _b !== void 0 ? _b : null;
|
|
206
|
+
return this.process?.stderr ?? null;
|
|
215
207
|
}
|
|
216
208
|
// Validate whether or not we have a specific codec available to us in FFmpeg.
|
|
217
209
|
static async codecEnabled(videoProcessor, codec, log) {
|
|
218
210
|
try {
|
|
219
211
|
// Promisify exec to allow us to wait for it asynchronously.
|
|
220
|
-
const execAsync =
|
|
212
|
+
const execAsync = util.promisify(execFile);
|
|
221
213
|
// Check for the codecs in FFmpeg.
|
|
222
214
|
const { stdout } = await execAsync(videoProcessor, ["-codecs"]);
|
|
223
215
|
// See if we can find the codec.
|
|
@@ -238,5 +230,4 @@ class FfmpegProcess extends events_1.EventEmitter {
|
|
|
238
230
|
return false;
|
|
239
231
|
}
|
|
240
232
|
}
|
|
241
|
-
exports.FfmpegProcess = FfmpegProcess;
|
|
242
233
|
//# sourceMappingURL=protect-ffmpeg.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"protect-ffmpeg.js","sourceRoot":"","sources":["../src/protect-ffmpeg.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"protect-ffmpeg.js","sourceRoot":"","sources":["../src/protect-ffmpeg.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { PlatformAccessory } from "homebridge";
|
|
2
|
+
import { ProtectLightConfig } from "unifi-protect";
|
|
3
|
+
import { ProtectDevice } from "./protect-device.js";
|
|
4
|
+
import { ProtectNvr } from "./protect-nvr.js";
|
|
5
|
+
export declare class ProtectLight extends ProtectDevice {
|
|
6
|
+
private lightState;
|
|
7
|
+
ufp: ProtectLightConfig;
|
|
8
|
+
constructor(nvr: ProtectNvr, device: ProtectLightConfig, accessory: PlatformAccessory);
|
|
9
|
+
private configureDevice;
|
|
10
|
+
private configureLightbulb;
|
|
11
|
+
private configureMqtt;
|
|
12
|
+
private eventHandler;
|
|
13
|
+
}
|
package/dist/protect-light.js
CHANGED
|
@@ -1,18 +1,20 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import { ProtectDevice } from "./protect-device.js";
|
|
2
|
+
export class ProtectLight extends ProtectDevice {
|
|
3
|
+
// Create an instance.
|
|
4
|
+
constructor(nvr, device, accessory) {
|
|
5
|
+
super(nvr, accessory);
|
|
6
|
+
this.lightState = false;
|
|
7
|
+
this.ufp = device;
|
|
8
|
+
this.configureHints();
|
|
9
|
+
this.configureDevice();
|
|
10
|
+
}
|
|
6
11
|
// Initialize and configure the light accessory for HomeKit.
|
|
7
|
-
|
|
8
|
-
var _a;
|
|
12
|
+
configureDevice() {
|
|
9
13
|
this.lightState = false;
|
|
10
|
-
// Save the device object before we wipeout the context.
|
|
11
|
-
const device = this.accessory.context.device;
|
|
12
14
|
// Clean out the context object in case it's been polluted somehow.
|
|
13
15
|
this.accessory.context = {};
|
|
14
|
-
this.accessory.context.
|
|
15
|
-
this.accessory.context.nvr =
|
|
16
|
+
this.accessory.context.mac = this.ufp.mac;
|
|
17
|
+
this.accessory.context.nvr = this.nvr.ufp.mac;
|
|
16
18
|
// Configure accessory information.
|
|
17
19
|
this.configureInfo();
|
|
18
20
|
// Configure the light.
|
|
@@ -21,91 +23,112 @@ class ProtectLight extends protect_accessory_1.ProtectAccessory {
|
|
|
21
23
|
this.configureMotionSensor();
|
|
22
24
|
// Configure MQTT services.
|
|
23
25
|
this.configureMqtt();
|
|
24
|
-
|
|
26
|
+
// Listen for events.
|
|
27
|
+
this.nvr.events.on("updateEvent." + this.ufp.id, this.listeners["updateEvent." + this.ufp.id] = this.eventHandler.bind(this));
|
|
28
|
+
return true;
|
|
25
29
|
}
|
|
26
30
|
// Configure the light for HomeKit.
|
|
27
31
|
configureLightbulb() {
|
|
28
|
-
var _a, _b;
|
|
29
32
|
// Find the service, if it exists.
|
|
30
33
|
let lightService = this.accessory.getService(this.hap.Service.Lightbulb);
|
|
31
34
|
// Add the service to the accessory, if needed.
|
|
32
35
|
if (!lightService) {
|
|
33
36
|
lightService = new this.hap.Service.Lightbulb(this.accessory.displayName);
|
|
34
37
|
if (!lightService) {
|
|
35
|
-
this.log.error("
|
|
38
|
+
this.log.error("Unable to add light.");
|
|
36
39
|
return false;
|
|
37
40
|
}
|
|
38
41
|
this.accessory.addService(lightService);
|
|
39
42
|
}
|
|
40
43
|
// Turn the light on or off.
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
+
lightService.getCharacteristic(this.hap.Characteristic.On)
|
|
45
|
+
?.onGet(() => {
|
|
46
|
+
return this.ufp.isLightOn === true;
|
|
47
|
+
})
|
|
48
|
+
.onSet(async (value) => {
|
|
44
49
|
const lightState = value === true;
|
|
45
|
-
const newDevice = await this.nvr.
|
|
50
|
+
const newDevice = await this.nvr.ufpApi.updateDevice(this.ufp, { lightOnSettings: { isLedForceOn: lightState } });
|
|
46
51
|
if (!newDevice) {
|
|
47
|
-
this.log.error("
|
|
52
|
+
this.log.error("Unable to turn the light %s. Please ensure this username has the Administrator role in UniFi Protect.", lightState ? "on" : "off");
|
|
48
53
|
return;
|
|
49
54
|
}
|
|
50
55
|
// Set the context to our updated device configuration.
|
|
51
|
-
this.
|
|
56
|
+
this.ufp = newDevice;
|
|
52
57
|
});
|
|
53
58
|
// Adjust the brightness of the light.
|
|
54
|
-
|
|
59
|
+
lightService.getCharacteristic(this.hap.Characteristic.Brightness)
|
|
60
|
+
?.onGet(() => {
|
|
55
61
|
// The Protect ledLevel settings goes from 1 - 6. HomeKit expects percentages, so we convert it like so.
|
|
56
|
-
return (this.
|
|
57
|
-
})
|
|
62
|
+
return (this.ufp.lightDeviceSettings.ledLevel - 1) * 20;
|
|
63
|
+
})
|
|
64
|
+
.onSet(async (value) => {
|
|
58
65
|
const brightness = Math.round((value / 20) + 1);
|
|
59
|
-
const newDevice = await this.nvr.
|
|
66
|
+
const newDevice = await this.nvr.ufpApi.updateDevice(this.ufp, { lightDeviceSettings: { ledLevel: brightness } });
|
|
60
67
|
if (!newDevice) {
|
|
61
|
-
this.log.error("
|
|
68
|
+
this.log.error("Unable to adjust the brightness to %s%. Please ensure this username has the Administrator role in UniFi Protect.", value);
|
|
62
69
|
return;
|
|
63
70
|
}
|
|
64
71
|
// Set the context to our updated device configuration.
|
|
65
|
-
this.
|
|
72
|
+
this.ufp = newDevice;
|
|
66
73
|
// Make sure we properly reflect what brightness we're actually at.
|
|
67
74
|
setTimeout(() => {
|
|
68
|
-
lightService
|
|
75
|
+
lightService?.updateCharacteristic(this.hap.Characteristic.Brightness, (brightness - 1) * 20);
|
|
69
76
|
}, 50);
|
|
70
77
|
});
|
|
71
78
|
// Initialize the light.
|
|
72
|
-
lightService.updateCharacteristic(this.hap.Characteristic.On, this.
|
|
73
|
-
lightService.updateCharacteristic(this.hap.Characteristic.Brightness, (this.
|
|
79
|
+
lightService.updateCharacteristic(this.hap.Characteristic.On, this.ufp.isLightOn);
|
|
80
|
+
lightService.updateCharacteristic(this.hap.Characteristic.Brightness, (this.ufp.lightDeviceSettings.ledLevel - 1) * 20);
|
|
74
81
|
return true;
|
|
75
82
|
}
|
|
76
83
|
// Configure MQTT capabilities of this light.
|
|
77
84
|
configureMqtt() {
|
|
78
|
-
var _a;
|
|
79
85
|
const lightService = this.accessory.getService(this.hap.Service.Lightbulb);
|
|
80
86
|
if (!lightService) {
|
|
81
87
|
return false;
|
|
82
88
|
}
|
|
83
89
|
// Trigger a motion event in MQTT, if requested to do so.
|
|
84
|
-
|
|
85
|
-
var _a, _b, _c;
|
|
90
|
+
this.nvr.mqtt?.subscribe(this.accessory, "light", (message) => {
|
|
86
91
|
const value = message.toString();
|
|
87
92
|
const brightness = parseInt(value);
|
|
88
|
-
switch (value
|
|
93
|
+
switch (value?.toLowerCase()) {
|
|
89
94
|
case "off":
|
|
90
|
-
|
|
91
|
-
this.log.info("
|
|
95
|
+
lightService.getCharacteristic(this.hap.Characteristic.On)?.setValue(false);
|
|
96
|
+
this.log.info("Light turned off via MQTT.");
|
|
92
97
|
break;
|
|
93
98
|
case "on":
|
|
94
|
-
|
|
95
|
-
this.log.info("
|
|
99
|
+
lightService.getCharacteristic(this.hap.Characteristic.On)?.setValue(true);
|
|
100
|
+
this.log.info("Light turned on via MQTT.");
|
|
96
101
|
break;
|
|
97
102
|
default:
|
|
98
103
|
// Unknown message - ignore it.
|
|
99
104
|
if (isNaN(brightness) || (brightness < 0) || (brightness > 100)) {
|
|
100
105
|
return;
|
|
101
106
|
}
|
|
102
|
-
|
|
103
|
-
this.log.info("
|
|
107
|
+
lightService.getCharacteristic(this.hap.Characteristic.Brightness)?.setValue(brightness);
|
|
108
|
+
this.log.info("Light set to %s% via MQTT.", brightness);
|
|
104
109
|
break;
|
|
105
110
|
}
|
|
106
111
|
});
|
|
107
112
|
return true;
|
|
108
113
|
}
|
|
114
|
+
// Handle light-related events.
|
|
115
|
+
eventHandler(packet) {
|
|
116
|
+
const payload = packet.payload;
|
|
117
|
+
// It's a motion event - process it accordingly.
|
|
118
|
+
if (payload.isPirMotionDetected && payload.lastMotion) {
|
|
119
|
+
this.nvr.events.motionEventHandler(this, payload.lastMotion);
|
|
120
|
+
}
|
|
121
|
+
// It's a light power event - process it accordingly.
|
|
122
|
+
if ("isLightOn" in payload) {
|
|
123
|
+
// Update our power state.
|
|
124
|
+
this.accessory.getService(this.hap.Service.Lightbulb)?.updateCharacteristic(this.hap.Characteristic.On, payload.isLightOn);
|
|
125
|
+
}
|
|
126
|
+
// It's light brightness event - process it accordingly.
|
|
127
|
+
if (payload.lightDeviceSettings && ("ledLevel" in payload.lightDeviceSettings)) {
|
|
128
|
+
// Update our brightness.
|
|
129
|
+
this.accessory.getService(this.hap.Service.Lightbulb)?.
|
|
130
|
+
updateCharacteristic(this.hap.Characteristic.Brightness, (payload.lightDeviceSettings.ledLevel - 1) * 20);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
109
133
|
}
|
|
110
|
-
exports.ProtectLight = ProtectLight;
|
|
111
134
|
//# sourceMappingURL=protect-light.js.map
|