homebridge-eufy-security 4.4.2-beta.2 → 4.4.2-beta.20
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/CHANGELOG.md +1 -1
- package/FUNDING.yml +1 -0
- package/README.md +14 -17
- package/dist/accessories/AutoSyncStationAccessory.js +26 -30
- package/dist/accessories/AutoSyncStationAccessory.js.map +1 -1
- package/dist/accessories/BaseAccessory.js +33 -46
- package/dist/accessories/BaseAccessory.js.map +1 -1
- package/dist/accessories/CameraAccessory.js +92 -167
- package/dist/accessories/CameraAccessory.js.map +1 -1
- package/dist/accessories/Device.js +11 -15
- package/dist/accessories/Device.js.map +1 -1
- package/dist/accessories/EntrySensorAccessory.js +7 -12
- package/dist/accessories/EntrySensorAccessory.js.map +1 -1
- package/dist/accessories/LockAccessory.js +20 -24
- package/dist/accessories/LockAccessory.js.map +1 -1
- package/dist/accessories/MotionSensorAccessory.js +7 -12
- package/dist/accessories/MotionSensorAccessory.js.map +1 -1
- package/dist/accessories/StationAccessory.js +40 -45
- package/dist/accessories/StationAccessory.js.map +1 -1
- package/dist/config.js +1 -4
- package/dist/config.js.map +1 -1
- package/dist/controller/LocalLivestreamManager.js +85 -89
- package/dist/controller/LocalLivestreamManager.js.map +1 -1
- package/dist/controller/recordingDelegate.js +116 -108
- package/dist/controller/recordingDelegate.js.map +1 -1
- package/dist/controller/snapshotDelegate.js +379 -0
- package/dist/controller/snapshotDelegate.js.map +1 -0
- package/dist/controller/streamingDelegate.js +202 -207
- package/dist/controller/streamingDelegate.js.map +1 -1
- package/dist/index.js +9 -7
- package/dist/index.js.map +1 -1
- package/dist/interfaces.js +1 -2
- package/dist/interfaces.js.map +1 -1
- package/dist/platform.js +145 -165
- package/dist/platform.js.map +1 -1
- package/dist/settings.js +26 -19
- package/dist/settings.js.map +1 -1
- package/dist/utils/Talkback.js +9 -13
- package/dist/utils/Talkback.js.map +1 -1
- package/dist/utils/configTypes.js +12 -7
- package/dist/utils/configTypes.js.map +1 -1
- package/dist/utils/ffmpeg.js +44 -52
- package/dist/utils/ffmpeg.js.map +1 -1
- package/dist/utils/interfaces.js +1 -5
- package/dist/utils/interfaces.js.map +1 -1
- package/dist/utils/utils.js +32 -134
- package/dist/utils/utils.js.map +1 -1
- package/dist/version.js +1 -4
- package/dist/version.js.map +1 -1
- package/eslint.config.mjs +25 -47
- package/homebridge-eufy-security.png +0 -0
- package/homebridge-ui/public/app.js +221 -0
- package/homebridge-ui/public/assets/devices/eufycam3pro_large.png +0 -0
- package/homebridge-ui/public/assets/devices/homebase3_large.png +0 -0
- package/homebridge-ui/public/assets/devices/homebasemini_large.jpg +0 -0
- package/homebridge-ui/public/assets/devices/indoorcamC210_large.png +0 -0
- package/homebridge-ui/public/assets/devices/indoorcamC220_large.png +0 -0
- package/homebridge-ui/public/assets/devices/indoorcamE30_large.png +0 -0
- package/homebridge-ui/public/assets/devices/solocamc35_large.png +0 -0
- package/homebridge-ui/public/assets/devices/solocame30_large.png +0 -0
- package/homebridge-ui/public/components/device-card.js +141 -0
- package/homebridge-ui/public/components/guard-modes.js +88 -0
- package/homebridge-ui/public/components/number-input.js +121 -0
- package/homebridge-ui/public/components/select.js +73 -0
- package/homebridge-ui/public/components/toggle.js +68 -0
- package/homebridge-ui/public/index.html +24 -14
- package/homebridge-ui/public/services/api.js +129 -0
- package/homebridge-ui/public/services/config.js +144 -0
- package/homebridge-ui/public/style.css +705 -0
- package/homebridge-ui/public/utils/countries.js +73 -0
- package/homebridge-ui/public/utils/device-images.js +69 -0
- package/homebridge-ui/public/utils/helpers.js +47 -0
- package/homebridge-ui/public/views/dashboard.js +211 -0
- package/homebridge-ui/public/views/device-detail.js +631 -0
- package/homebridge-ui/public/views/login.js +625 -0
- package/homebridge-ui/public/views/settings.js +389 -0
- package/homebridge-ui/public/views/unsupported-detail.js +216 -0
- package/homebridge-ui/server.js +567 -450
- package/package.json +19 -59
- package/dist/accessories/AutoSyncStationAccessory.d.ts +0 -42
- package/dist/accessories/AutoSyncStationAccessory.d.ts.map +0 -1
- package/dist/accessories/BaseAccessory.d.ts +0 -56
- package/dist/accessories/BaseAccessory.d.ts.map +0 -1
- package/dist/accessories/CameraAccessory.d.ts +0 -84
- package/dist/accessories/CameraAccessory.d.ts.map +0 -1
- package/dist/accessories/Device.d.ts +0 -19
- package/dist/accessories/Device.d.ts.map +0 -1
- package/dist/accessories/EntrySensorAccessory.d.ts +0 -25
- package/dist/accessories/EntrySensorAccessory.d.ts.map +0 -1
- package/dist/accessories/LockAccessory.d.ts +0 -45
- package/dist/accessories/LockAccessory.d.ts.map +0 -1
- package/dist/accessories/MotionSensorAccessory.d.ts +0 -25
- package/dist/accessories/MotionSensorAccessory.d.ts.map +0 -1
- package/dist/accessories/StationAccessory.d.ts +0 -86
- package/dist/accessories/StationAccessory.d.ts.map +0 -1
- package/dist/config.d.ts +0 -31
- package/dist/config.d.ts.map +0 -1
- package/dist/controller/LocalLivestreamManager.d.ts +0 -31
- package/dist/controller/LocalLivestreamManager.d.ts.map +0 -1
- package/dist/controller/SnapshotManager.d.ts +0 -64
- package/dist/controller/SnapshotManager.d.ts.map +0 -1
- package/dist/controller/SnapshotManager.js +0 -434
- package/dist/controller/SnapshotManager.js.map +0 -1
- package/dist/controller/recordingDelegate.d.ts +0 -27
- package/dist/controller/recordingDelegate.d.ts.map +0 -1
- package/dist/controller/streamingDelegate.d.ts +0 -52
- package/dist/controller/streamingDelegate.d.ts.map +0 -1
- package/dist/index.d.ts +0 -7
- package/dist/index.d.ts.map +0 -1
- package/dist/interfaces.d.ts +0 -15
- package/dist/interfaces.d.ts.map +0 -1
- package/dist/platform.d.ts +0 -94
- package/dist/platform.d.ts.map +0 -1
- package/dist/settings.d.ts +0 -23
- package/dist/settings.d.ts.map +0 -1
- package/dist/utils/Talkback.d.ts +0 -21
- package/dist/utils/Talkback.d.ts.map +0 -1
- package/dist/utils/configTypes.d.ts +0 -68
- package/dist/utils/configTypes.d.ts.map +0 -1
- package/dist/utils/ffmpeg.d.ts +0 -111
- package/dist/utils/ffmpeg.d.ts.map +0 -1
- package/dist/utils/interfaces.d.ts +0 -8
- package/dist/utils/interfaces.d.ts.map +0 -1
- package/dist/utils/utils.d.ts +0 -35
- package/dist/utils/utils.d.ts.map +0 -1
- package/dist/version.d.ts +0 -2
- package/dist/version.d.ts.map +0 -1
- package/homebridge-ui/configui/app/util/types.d.ts +0 -65
- package/homebridge-ui/configui/app/util/types.d.ts.map +0 -1
- package/homebridge-ui/configui/app/util/types.js +0 -18
- package/homebridge-ui/configui/app/util/types.js.map +0 -1
- package/homebridge-ui/public/3rdpartylicenses.txt +0 -561
- package/homebridge-ui/public/assets/devices/homebase2_large.jpg +0 -0
- package/homebridge-ui/public/assets/devices/homebase3_large.jpg +0 -0
- package/homebridge-ui/public/assets/images/homebridge-eufy-security.png +0 -0
- package/homebridge-ui/public/favicon.ico +0 -0
- package/homebridge-ui/public/main.f6423287e0411c87.js +0 -1
- package/homebridge-ui/public/polyfills.efa2f09ba3e49167.js +0 -1
- package/homebridge-ui/public/runtime.ccde331d62c423b3.js +0 -1
- package/homebridge-ui/public/styles.13e635bf7a488639.css +0 -5
- package/homebridge-ui/server.d.ts +0 -2
- package/homebridge-ui/server.d.ts.map +0 -1
- package/homebridge-ui/server.js.map +0 -1
- package/homebridge-ui/version.d.ts +0 -2
- package/homebridge-ui/version.d.ts.map +0 -1
- package/homebridge-ui/version.js +0 -5
- package/homebridge-ui/version.js.map +0 -1
|
@@ -1,117 +1,113 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
// Define a class for the local livestream manager.
|
|
6
|
-
class LocalLivestreamManager extends stream_1.EventEmitter {
|
|
7
|
-
camera;
|
|
8
|
-
CONNECTION_ESTABLISHED_TIMEOUT = 5;
|
|
1
|
+
import { Deferred } from '../utils/utils.js';
|
|
2
|
+
const P2P_TIMEOUT_MS = 15_000;
|
|
3
|
+
const DUPLICATE_STREAM_GUARD_S = 5;
|
|
4
|
+
export class LocalLivestreamManager {
|
|
9
5
|
stationStream = null;
|
|
10
|
-
|
|
11
|
-
livestreamIsStarting = false;
|
|
6
|
+
pending = null;
|
|
12
7
|
eufyClient;
|
|
13
8
|
log;
|
|
14
|
-
|
|
9
|
+
serialNumber;
|
|
15
10
|
constructor(camera) {
|
|
16
|
-
super();
|
|
17
|
-
this.camera = camera;
|
|
18
11
|
this.eufyClient = camera.platform.eufyClient;
|
|
19
|
-
this.
|
|
12
|
+
this.serialNumber = camera.device.getSerial();
|
|
20
13
|
this.log = camera.log;
|
|
21
|
-
this.
|
|
22
|
-
this.eufyClient.on('station livestream start', this.onStationLivestreamStart
|
|
23
|
-
this.eufyClient.on('station livestream stop', this.onStationLivestreamStop
|
|
14
|
+
this.log.debug(`LocalLivestreamManager initialized for ${camera.device.getName()} (serial: ${this.serialNumber})`);
|
|
15
|
+
this.eufyClient.on('station livestream start', this.onStationLivestreamStart);
|
|
16
|
+
this.eufyClient.on('station livestream stop', this.onStationLivestreamStop);
|
|
24
17
|
}
|
|
25
|
-
|
|
26
|
-
|
|
18
|
+
/** Destroy active streams and reset state. */
|
|
19
|
+
destroyStreams() {
|
|
27
20
|
if (this.stationStream) {
|
|
28
21
|
this.stationStream.audiostream.unpipe();
|
|
29
22
|
this.stationStream.audiostream.destroy();
|
|
30
23
|
this.stationStream.videostream.unpipe();
|
|
31
24
|
this.stationStream.videostream.destroy();
|
|
25
|
+
this.stationStream = null;
|
|
32
26
|
}
|
|
33
|
-
this.stationStream = null;
|
|
34
|
-
this.livestreamStartedAt = null;
|
|
35
27
|
}
|
|
36
|
-
|
|
37
|
-
async
|
|
38
|
-
this.log.debug('New instance requests livestream.');
|
|
28
|
+
/** Return the active livestream, or start a new one. Concurrent callers share the same pending request. */
|
|
29
|
+
async getLocalLiveStream() {
|
|
39
30
|
if (this.stationStream) {
|
|
40
|
-
const runtime = (Date.now() - this.
|
|
41
|
-
this.log.debug(
|
|
31
|
+
const runtime = ((Date.now() - this.stationStream.createdAt) / 1000).toFixed(1);
|
|
32
|
+
this.log.debug(`Reusing livestream started ${runtime}s ago.`);
|
|
42
33
|
return this.stationStream;
|
|
43
34
|
}
|
|
44
|
-
|
|
45
|
-
|
|
35
|
+
return this.startLocalLiveStream();
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Requests a P2P livestream from the eufy station and waits for the
|
|
39
|
+
* 'station livestream start' event. If a start is already in progress,
|
|
40
|
+
* the caller piggy-backs on the existing promise instead of issuing a
|
|
41
|
+
* duplicate request.
|
|
42
|
+
*/
|
|
43
|
+
startLocalLiveStream() {
|
|
44
|
+
if (this.pending) {
|
|
45
|
+
this.log.debug('Livestream already starting — waiting on existing request.');
|
|
46
|
+
return this.pending.deferred.promise;
|
|
47
|
+
}
|
|
48
|
+
this.log.debug(`Starting station livestream for serial: ${this.serialNumber}...`);
|
|
49
|
+
const deferred = new Deferred();
|
|
50
|
+
const timer = setTimeout(() => {
|
|
51
|
+
this.log.error(`Livestream timeout: no P2P stream event received within ${P2P_TIMEOUT_MS / 1000}s for serial ${this.serialNumber}.`);
|
|
52
|
+
this.log.warn('If using a recent Node.js version, try enabling "Embedded PKCS1 Support" in the plugin settings.');
|
|
53
|
+
this.settlePending('reject', 'Livestream timeout — try enabling Embedded PKCS1 Support in settings.');
|
|
54
|
+
}, P2P_TIMEOUT_MS);
|
|
55
|
+
this.pending = { deferred, timer };
|
|
56
|
+
try {
|
|
57
|
+
this.eufyClient.startStationLivestream(this.serialNumber);
|
|
46
58
|
}
|
|
59
|
+
catch (err) {
|
|
60
|
+
this.log.error(`startStationLivestream threw: ${err}`);
|
|
61
|
+
this.settlePending('reject', err);
|
|
62
|
+
}
|
|
63
|
+
return deferred.promise;
|
|
47
64
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
this.log.error('Livestream timeout: No livestream emitted within the expected timeframe.');
|
|
62
|
-
const problematicNodeVersions = ['18.19.1', '20.11.1', '21.6.2'];
|
|
63
|
-
this.log.warn(`If you are using Node.js version ${problematicNodeVersions.join(', ')} or newer, this might be related to RSA_PKCS1_PADDING support removal.`);
|
|
64
|
-
this.log.warn('Please try enabling "Embedded PKCS1 Support" in the plugin settings to resolve this issue.');
|
|
65
|
-
this.stopLocalLiveStream();
|
|
66
|
-
this.livestreamIsStarting = false;
|
|
67
|
-
reject('No livestream emitted... This may be due to Node.js compatibility issues. Try enabling Embedded PKCS1 Support in settings.');
|
|
68
|
-
}, 15 * 1000 // After 15 seconds, Apple HomeKit may disconnect, invalidating the livestream setup.
|
|
69
|
-
);
|
|
70
|
-
this.once('livestream start', async () => {
|
|
71
|
-
if (this.stationStream !== null) {
|
|
72
|
-
this.log.debug('New livestream started.');
|
|
73
|
-
clearTimeout(hardStop);
|
|
74
|
-
this.livestreamIsStarting = false;
|
|
75
|
-
resolve(this.stationStream);
|
|
76
|
-
}
|
|
77
|
-
else {
|
|
78
|
-
reject('no started livestream found');
|
|
79
|
-
}
|
|
80
|
-
});
|
|
81
|
-
});
|
|
65
|
+
settlePending(action, payload) {
|
|
66
|
+
const p = this.pending;
|
|
67
|
+
if (!p)
|
|
68
|
+
return;
|
|
69
|
+
clearTimeout(p.timer);
|
|
70
|
+
this.pending = null;
|
|
71
|
+
if (action === 'resolve') {
|
|
72
|
+
p.deferred.resolve(payload);
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
p.deferred.reject(payload instanceof Error ? payload : new Error(String(payload)));
|
|
76
|
+
this.stopLocalLiveStream();
|
|
77
|
+
}
|
|
82
78
|
}
|
|
83
|
-
// Stop the local livestream.
|
|
84
79
|
stopLocalLiveStream() {
|
|
85
80
|
this.log.debug('Stopping station livestream.');
|
|
86
|
-
this.eufyClient.stopStationLivestream(this.
|
|
87
|
-
this.
|
|
81
|
+
this.eufyClient.stopStationLivestream(this.serialNumber);
|
|
82
|
+
this.destroyStreams();
|
|
88
83
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
this.log.debug(`${station.getName()} station livestream for ${device.getName()} has stopped.`);
|
|
93
|
-
this.initialize();
|
|
94
|
-
}
|
|
84
|
+
/** True when the event belongs to this camera instance. */
|
|
85
|
+
isOwnDevice(device) {
|
|
86
|
+
return device.getSerial() === this.serialNumber;
|
|
95
87
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
88
|
+
onStationLivestreamStop = (_station, device) => {
|
|
89
|
+
if (!this.isOwnDevice(device))
|
|
90
|
+
return;
|
|
91
|
+
this.log.debug(`Station livestream for ${device.getName()} has stopped.`);
|
|
92
|
+
this.destroyStreams();
|
|
93
|
+
};
|
|
94
|
+
onStationLivestreamStart = (station, device, metadata, videostream, audiostream) => {
|
|
95
|
+
if (!this.isOwnDevice(device))
|
|
96
|
+
return;
|
|
97
|
+
// Guard against duplicate events fired in quick succession.
|
|
98
|
+
if (this.stationStream) {
|
|
99
|
+
const elapsed = (Date.now() - this.stationStream.createdAt) / 1000;
|
|
100
|
+
if (elapsed < DUPLICATE_STREAM_GUARD_S) {
|
|
101
|
+
this.log.warn('Duplicate livestream event received — ignoring.');
|
|
102
|
+
return;
|
|
105
103
|
}
|
|
106
|
-
this.initialize(); // important to prevent unwanted behaviour when the eufy station emits the 'livestream start' event multiple times
|
|
107
|
-
this.log.debug(station.getName() + ' station livestream (P2P session) for ' + device.getName() + ' has started.');
|
|
108
|
-
this.livestreamStartedAt = Date.now();
|
|
109
|
-
const createdAt = Date.now();
|
|
110
|
-
this.stationStream = { station, device, metadata, videostream, audiostream, createdAt };
|
|
111
|
-
this.log.debug('Stream metadata: ', this.stationStream.metadata);
|
|
112
|
-
this.emit('livestream start');
|
|
113
104
|
}
|
|
114
|
-
|
|
105
|
+
// Tear down any prior stream before storing the new one.
|
|
106
|
+
this.destroyStreams();
|
|
107
|
+
this.log.debug(`${station.getName()} P2P livestream for ${device.getName()} started.`);
|
|
108
|
+
this.log.debug('Stream metadata:', JSON.stringify(metadata));
|
|
109
|
+
this.stationStream = { videostream, audiostream, createdAt: Date.now() };
|
|
110
|
+
this.settlePending('resolve', this.stationStream);
|
|
111
|
+
};
|
|
115
112
|
}
|
|
116
|
-
exports.LocalLivestreamManager = LocalLivestreamManager;
|
|
117
113
|
//# sourceMappingURL=LocalLivestreamManager.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LocalLivestreamManager.js","sourceRoot":"","sources":["../../src/
|
|
1
|
+
{"version":3,"file":"LocalLivestreamManager.js","sourceRoot":"","sources":["../../src/controller/LocalLivestreamManager.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,9 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const eufy_security_client_1 = require("eufy-security-client");
|
|
5
|
-
const ffmpeg_1 = require("../utils/ffmpeg");
|
|
6
|
-
const utils_1 = require("../utils/utils");
|
|
1
|
+
import { PropertyName } from 'eufy-security-client';
|
|
2
|
+
import { FFmpeg, FFmpegParameters } from '../utils/ffmpeg.js';
|
|
3
|
+
import { CHAR, SERV, isRtspReady, log } from '../utils/utils.js';
|
|
7
4
|
const MAX_RECORDING_MINUTES = 1; // should never be used
|
|
8
5
|
const HKSVQuitReason = [
|
|
9
6
|
'Normal',
|
|
@@ -17,7 +14,7 @@ const HKSVQuitReason = [
|
|
|
17
14
|
'Protocol error',
|
|
18
15
|
'Invalid Configuration',
|
|
19
16
|
];
|
|
20
|
-
class RecordingDelegate {
|
|
17
|
+
export class RecordingDelegate {
|
|
21
18
|
platform;
|
|
22
19
|
accessory;
|
|
23
20
|
camera;
|
|
@@ -42,149 +39,160 @@ class RecordingDelegate {
|
|
|
42
39
|
isRecording() {
|
|
43
40
|
return this.handlingStreamingRequest;
|
|
44
41
|
}
|
|
42
|
+
resetMotionSensor() {
|
|
43
|
+
const motionDetected = this.accessory
|
|
44
|
+
.getService(SERV.MotionSensor)?.getCharacteristic(CHAR.MotionDetected).value;
|
|
45
|
+
if (motionDetected) {
|
|
46
|
+
this.accessory
|
|
47
|
+
.getService(SERV.MotionSensor)?.getCharacteristic(CHAR.MotionDetected)
|
|
48
|
+
.updateValue(false);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
clearForceStopTimeout() {
|
|
52
|
+
if (this.forceStopTimeout) {
|
|
53
|
+
clearTimeout(this.forceStopTimeout);
|
|
54
|
+
this.forceStopTimeout = undefined;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
isMotionDetected() {
|
|
58
|
+
return !!this.accessory
|
|
59
|
+
.getService(SERV.MotionSensor)?.getCharacteristic(CHAR.MotionDetected).value;
|
|
60
|
+
}
|
|
61
|
+
async configureInputSource(videoParams, audioParams) {
|
|
62
|
+
if (isRtspReady(this.camera, this.cameraConfig)) {
|
|
63
|
+
const url = this.camera.getPropertyValue(PropertyName.DeviceRTSPStreamUrl);
|
|
64
|
+
log.debug(this.camera.getName(), 'RTSP URL: ' + url);
|
|
65
|
+
videoParams.setInputSource(url);
|
|
66
|
+
audioParams.setInputSource(url);
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
const streamData = await this.localLivestreamManager.getLocalLiveStream();
|
|
70
|
+
await videoParams.setInputStream(streamData.videostream);
|
|
71
|
+
await audioParams.setInputStream(streamData.audiostream);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
45
74
|
async *handleRecordingStreamRequest() {
|
|
46
75
|
this.handlingStreamingRequest = true;
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
let filebuffer = Buffer.alloc(0);
|
|
76
|
+
this.closeReason = undefined;
|
|
77
|
+
log.info(this.camera.getName(), 'requesting recording for HomeKit Secure Video.');
|
|
50
78
|
try {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
else {
|
|
56
|
-
utils_1.log.debug('HKSV and plugin are set to omit audio recording.');
|
|
79
|
+
if (!this.configuration) {
|
|
80
|
+
log.error(this.camera.getName(), 'No recording configuration available. Aborting.');
|
|
81
|
+
yield { data: Buffer.alloc(0), isLast: true };
|
|
82
|
+
return;
|
|
57
83
|
}
|
|
58
|
-
const
|
|
59
|
-
|
|
84
|
+
const audioEnabled = this.controller?.recordingManagement?.recordingManagementService.getCharacteristic(CHAR.RecordingAudioActive).value;
|
|
85
|
+
log.debug(this.camera.getName(), `HKSV audio recording: ${audioEnabled ? 'enabled' : 'disabled'}.`);
|
|
86
|
+
const videoParams = await FFmpegParameters.forVideoRecording();
|
|
87
|
+
const audioParams = await FFmpegParameters.forAudioRecording();
|
|
60
88
|
const videoConfig = this.cameraConfig.videoConfig ?? {};
|
|
61
|
-
if (this.cameraConfig.videoConfig && this.cameraConfig.videoConfig.videoProcessor) {
|
|
62
|
-
videoConfig.videoProcessor = this.cameraConfig.videoConfig.videoProcessor;
|
|
63
|
-
}
|
|
64
89
|
videoParams.setupForRecording(videoConfig, this.configuration);
|
|
65
90
|
audioParams.setupForRecording(videoConfig, this.configuration);
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
const url = this.camera.getPropertyValue(eufy_security_client_1.PropertyName.DeviceRTSPStreamUrl);
|
|
69
|
-
utils_1.log.debug(this.camera.getName(), 'RTSP URL: ' + url);
|
|
70
|
-
videoParams.setInputSource(url);
|
|
71
|
-
audioParams.setInputSource(url);
|
|
72
|
-
}
|
|
73
|
-
else {
|
|
74
|
-
const streamData = await this.localLivestreamManager.getLocalLivestream().catch((error) => {
|
|
75
|
-
throw error;
|
|
76
|
-
});
|
|
77
|
-
await videoParams.setInputStream(streamData.videostream);
|
|
78
|
-
await audioParams.setInputStream(streamData.audiostream);
|
|
79
|
-
}
|
|
80
|
-
const ffmpeg = new ffmpeg_1.FFmpeg(`[${this.camera.getName()}] [HSV Recording Process]`, audioEnabled ? [videoParams, audioParams] : videoParams);
|
|
91
|
+
await this.configureInputSource(videoParams, audioParams);
|
|
92
|
+
const ffmpeg = new FFmpeg(`[${this.camera.getName()}] [HSV Recording Process]`, audioEnabled ? [videoParams, audioParams] : videoParams);
|
|
81
93
|
this.session = await ffmpeg.startFragmentedMP4Session();
|
|
82
|
-
|
|
83
|
-
if (
|
|
84
|
-
timer = this.platform.config.CameraMaxLivestreamDuration;
|
|
85
|
-
}
|
|
86
|
-
if (timer > 0) {
|
|
94
|
+
const maxDuration = Math.min(this.cameraConfig.hsvRecordingDuration ?? MAX_RECORDING_MINUTES * 60, this.platform.config.CameraMaxLivestreamDuration);
|
|
95
|
+
if (maxDuration > 0) {
|
|
87
96
|
this.forceStopTimeout = setTimeout(() => {
|
|
88
|
-
|
|
89
|
-
this.
|
|
90
|
-
|
|
91
|
-
.updateValue(false);
|
|
92
|
-
}, timer * 1000);
|
|
93
|
-
}
|
|
94
|
-
for await (const box of this.session.generator) {
|
|
95
|
-
if (!this.handlingStreamingRequest) {
|
|
96
|
-
utils_1.log.debug(this.camera.getName(), 'Recording was ended prematurely.');
|
|
97
|
-
break;
|
|
98
|
-
}
|
|
99
|
-
const { header, type, data } = box;
|
|
100
|
-
pending.push(header, data);
|
|
101
|
-
const motionDetected = this.accessory
|
|
102
|
-
.getService(utils_1.SERV.MotionSensor)?.getCharacteristic(utils_1.CHAR.MotionDetected).value;
|
|
103
|
-
if (type === 'moov' || type === 'mdat') {
|
|
104
|
-
const fragment = Buffer.concat(pending);
|
|
105
|
-
filebuffer = Buffer.concat([filebuffer, Buffer.concat(pending)]);
|
|
106
|
-
pending = [];
|
|
107
|
-
yield {
|
|
108
|
-
data: fragment,
|
|
109
|
-
isLast: !motionDetected,
|
|
110
|
-
};
|
|
111
|
-
if (!motionDetected) {
|
|
112
|
-
utils_1.log.debug(this.camera.getName(), 'Ending recording session due to motion stopped!');
|
|
113
|
-
break;
|
|
114
|
-
}
|
|
115
|
-
}
|
|
97
|
+
log.warn(this.camera.getName(), `Recording force-stopped after ${maxDuration}s.`);
|
|
98
|
+
this.resetMotionSensor();
|
|
99
|
+
}, maxDuration * 1000);
|
|
116
100
|
}
|
|
101
|
+
yield* this.generateFragments(this.session.generator);
|
|
117
102
|
}
|
|
118
103
|
catch (error) {
|
|
119
104
|
if (!this.handlingStreamingRequest && this.closeReason && this.closeReason === 3 /* HDSProtocolSpecificErrorReason.CANCELLED */) {
|
|
120
|
-
|
|
105
|
+
log.debug(this.camera.getName(), 'Recording encountered an error but that is expected, as the recording was canceled beforehand. Error: ' + error);
|
|
121
106
|
}
|
|
122
107
|
else {
|
|
123
|
-
|
|
108
|
+
log.error(this.camera.getName(), 'Error while recording: ' + error);
|
|
124
109
|
}
|
|
125
110
|
}
|
|
126
111
|
finally {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
112
|
+
this.logCloseReason();
|
|
113
|
+
this.clearForceStopTimeout();
|
|
114
|
+
this.resetMotionSensor();
|
|
115
|
+
this.localLivestreamManager.stopLocalLiveStream();
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
logCloseReason() {
|
|
119
|
+
if (!this.closeReason) {
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
if (this.closeReason === 3 /* HDSProtocolSpecificErrorReason.CANCELLED */) {
|
|
123
|
+
log.debug(this.camera.getName(), 'The recording process was canceled by the HomeKit Controller.');
|
|
124
|
+
}
|
|
125
|
+
else if (this.closeReason !== 0 /* HDSProtocolSpecificErrorReason.NORMAL */) {
|
|
126
|
+
log.warn(this.camera.getName(), `The recording process was aborted by HSV with reason "${HKSVQuitReason[this.closeReason]}"`);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Assembles fragmented MP4 boxes into HKSV-compatible recording packets.
|
|
131
|
+
* Yields an initialization segment (ftyp+moov), then paired moof+mdat fragments.
|
|
132
|
+
*/
|
|
133
|
+
async *generateFragments(generator) {
|
|
134
|
+
const cameraName = this.camera.getName();
|
|
135
|
+
let initPending = [];
|
|
136
|
+
let moofBuffer = null;
|
|
137
|
+
let isInit = true;
|
|
138
|
+
let fragmentCount = 0;
|
|
139
|
+
for await (const { header, type, data } of generator) {
|
|
140
|
+
if (!this.handlingStreamingRequest) {
|
|
141
|
+
log.debug(cameraName, 'Recording was ended prematurely.');
|
|
142
|
+
break;
|
|
133
143
|
}
|
|
134
|
-
if (
|
|
135
|
-
|
|
144
|
+
if (isInit) {
|
|
145
|
+
initPending.push(header, data);
|
|
146
|
+
if (type === 'moov') {
|
|
147
|
+
const fragment = Buffer.concat(initPending);
|
|
148
|
+
initPending = [];
|
|
149
|
+
isInit = false;
|
|
150
|
+
log.debug(cameraName, `HKSV: Sending initialization segment, size: ${fragment.length}`);
|
|
151
|
+
yield { data: fragment, isLast: false };
|
|
152
|
+
}
|
|
153
|
+
continue;
|
|
136
154
|
}
|
|
137
|
-
if (
|
|
138
|
-
|
|
139
|
-
this.forceStopTimeout = undefined;
|
|
155
|
+
if (type === 'moof') {
|
|
156
|
+
moofBuffer = Buffer.concat([header, data]);
|
|
140
157
|
}
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
158
|
+
else if (type === 'mdat' && moofBuffer) {
|
|
159
|
+
const fragment = Buffer.concat([moofBuffer, header, data]);
|
|
160
|
+
moofBuffer = null;
|
|
161
|
+
fragmentCount++;
|
|
162
|
+
log.debug(cameraName, `HKSV: Fragment #${fragmentCount}, size: ${fragment.length}`);
|
|
163
|
+
yield { data: fragment, isLast: false };
|
|
164
|
+
if (!this.isMotionDetected()) {
|
|
165
|
+
log.debug(cameraName, 'Ending recording session due to motion stopped.');
|
|
166
|
+
break;
|
|
167
|
+
}
|
|
148
168
|
}
|
|
149
|
-
this.localLivestreamManager.stopLocalLiveStream();
|
|
150
169
|
}
|
|
151
170
|
}
|
|
152
171
|
updateRecordingActive(active) {
|
|
153
|
-
|
|
172
|
+
log.debug(`Recording: ${active}`, this.accessory.displayName);
|
|
154
173
|
}
|
|
155
174
|
updateRecordingConfiguration(configuration) {
|
|
156
175
|
this.configuration = configuration;
|
|
157
176
|
}
|
|
158
177
|
closeRecordingStream(streamId, reason) {
|
|
159
|
-
|
|
178
|
+
log.info(this.camera.getName(), 'Closing recording process');
|
|
160
179
|
if (this.session) {
|
|
161
|
-
|
|
180
|
+
log.debug(this.camera.getName(), 'Stopping recording session.');
|
|
162
181
|
this.session.socket?.destroy();
|
|
163
182
|
this.session.process?.kill('SIGKILL');
|
|
164
183
|
this.session = undefined;
|
|
165
184
|
}
|
|
166
185
|
else {
|
|
167
|
-
|
|
168
|
-
}
|
|
169
|
-
if (this.forceStopTimeout) {
|
|
170
|
-
clearTimeout(this.forceStopTimeout);
|
|
171
|
-
this.forceStopTimeout = undefined;
|
|
172
|
-
}
|
|
173
|
-
// check whether motion is still in progress
|
|
174
|
-
const motionDetected = this.accessory
|
|
175
|
-
.getService(utils_1.SERV.MotionSensor)?.getCharacteristic(utils_1.CHAR.MotionDetected).value;
|
|
176
|
-
if (motionDetected) {
|
|
177
|
-
this.accessory
|
|
178
|
-
.getService(utils_1.SERV.MotionSensor)?.getCharacteristic(utils_1.CHAR.MotionDetected)
|
|
179
|
-
.updateValue(false);
|
|
186
|
+
log.warn('Recording session could not be closed gracefully.');
|
|
180
187
|
}
|
|
188
|
+
this.clearForceStopTimeout();
|
|
189
|
+
this.resetMotionSensor();
|
|
181
190
|
this.closeReason = reason;
|
|
182
191
|
this.handlingStreamingRequest = false;
|
|
183
192
|
}
|
|
184
193
|
acknowledgeStream(streamId) {
|
|
185
|
-
|
|
194
|
+
log.debug('end of recording acknowledged!');
|
|
186
195
|
this.closeRecordingStream(streamId, undefined);
|
|
187
196
|
}
|
|
188
197
|
}
|
|
189
|
-
exports.RecordingDelegate = RecordingDelegate;
|
|
190
198
|
//# sourceMappingURL=recordingDelegate.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"recordingDelegate.js","sourceRoot":"","sources":["../../src/
|
|
1
|
+
{"version":3,"file":"recordingDelegate.js","sourceRoot":"","sources":["../../src/controller/recordingDelegate.ts"],"names":[],"mappings":"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"}
|