homebridge-eufy-security-mikebcbc
Version:
Control Eufy Security from homebridge.
196 lines • 10.4 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.RecordingDelegate = void 0;
const eufy_security_client_1 = require("eufy-security-client");
const ffmpeg_record_1 = require("../utils/ffmpeg-record");
const ffmpeg_params_1 = require("../utils/ffmpeg-params");
const utils_1 = require("../utils/utils");
const MAX_RECORDING_MINUTES = 3;
const HKSVQuitReason = [
'Normal',
'Not allowed',
'Busy',
'Cancelled',
'Unsupported',
'Unexpected Failure',
'Timeout',
'Bad data',
'Protocol error',
'Invalid Configuration',
];
class RecordingDelegate {
constructor(streamingDelegate, accessory) {
this.streamingDelegate = streamingDelegate;
this.accessory = accessory;
this.handlingStreamingRequest = false;
this.platform = this.streamingDelegate.platform;
this.camera = this.streamingDelegate.device;
this.cameraName = this.streamingDelegate.cameraName;
this.cameraConfig = this.streamingDelegate.cameraConfig;
this.localLivestreamManager = this.streamingDelegate.getLivestreamManager();
this.log = this.platform.log;
}
setController(controller) {
this.controller = controller;
}
isRecording() {
return this.handlingStreamingRequest;
}
async *handleRecordingStreamRequest(streamId) {
var _a, _b, _c, _d, _e;
this.handlingStreamingRequest = true;
this.log.info(`${this.cameraName} requesting recording for HomeKit Secure Video. ID: ${streamId}`);
try {
// eslint-disable-next-line max-len
const audioEnabled = (_b = (_a = this.controller) === null || _a === void 0 ? void 0 : _a.recordingManagement) === null || _b === void 0 ? void 0 : _b.recordingManagementService.getCharacteristic(this.platform.Characteristic.RecordingAudioActive).value;
if (audioEnabled) {
this.log.debug('HKSV and plugin are set to record audio.');
}
else {
this.log.debug('HKSV and plugin are set to omit audio recording.');
}
const videoParams = await ffmpeg_params_1.FFmpegParameters.create({ type: 'videoRecording', debug: true });
const audioParams = await ffmpeg_params_1.FFmpegParameters.create({ type: 'audioRecording', debug: true });
videoParams.setupForRecording(this.cameraConfig.videoConfig || {}, this.configuration);
audioParams.setupForRecording(this.cameraConfig.videoConfig || {}, this.configuration);
const rtsp = (0, utils_1.is_rtsp_ready)(this.camera, this.cameraConfig, this.log);
let streamData = null;
if (rtsp) {
const url = this.camera.getPropertyValue(eufy_security_client_1.PropertyName.DeviceRTSPStreamUrl);
this.platform.log.debug(this.cameraName, 'RTSP URL: ' + url);
videoParams.setInputSource(url);
audioParams === null || audioParams === void 0 ? void 0 : audioParams.setInputSource(url);
}
else {
const value = await this.localLivestreamManager.getLocalLivestream()
.catch((err) => {
throw (this.cameraName + ' Unable to start the livestream: ' + err);
});
streamData = value;
videoParams.setInputSource('pipe:3');
audioParams === null || audioParams === void 0 ? void 0 : audioParams.setInputSource('pipe:4');
}
this.log.debug(this.cameraName, 'FFMPEG Process definition!');
this.session = new ffmpeg_record_1.FFmpegRecord(`[${this.cameraName}] [HSV Recording Process]`, audioEnabled ? [videoParams, audioParams] : [videoParams], this.platform.ffmpegLogger);
this.log.debug(this.cameraName, 'startFragmentedMP4Session Start!');
await this.session.start();
this.log.debug(this.cameraName, 'startFragmentedMP4Session Finish!');
if (this.session.process && this.session.process.stdio) {
// stdio is defined and can be used
this.log.debug(this.cameraName, 'stdio is defined and can be used!');
if (streamData !== null) {
this.log.debug(this.cameraName, 'Stream Data!');
streamData.videostream.pipe(this.session.process.stdio[3]);
if (audioEnabled) {
streamData.audiostream.pipe(this.session.process.stdio[4]);
}
}
}
let timer = MAX_RECORDING_MINUTES * 60;
if (this.platform.config.CameraMaxLivestreamDuration < timer) {
timer = this.platform.config.CameraMaxLivestreamDuration;
}
if (timer > 0) {
this.forceStopTimeout = setTimeout(() => {
var _a;
this.log.warn(this.cameraName, `The recording process has been running for ${timer} seconds and is now being forced closed!`);
(_a = this.accessory
.getService(this.platform.Service.MotionSensor)) === null || _a === void 0 ? void 0 : _a.getCharacteristic(this.platform.Characteristic.MotionDetected).updateValue(false);
}, timer * 1000);
}
let isMotionActive = true;
setTimeout(() => {
isMotionActive = false;
this.log.debug(this.cameraName, 'Ending recording session due to motion stopped!');
}, 60 * 1000); // Set the flag to false after 15 seconds
this.log.debug(this.cameraName, 'Generator Start!');
for await (const segment of this.session.segmentGenerator()) {
if (!this.isRecording()) {
this.log.debug(this.cameraName, 'Recording was ended prematurely.');
break;
}
if (!segment) {
continue;
}
this.log.debug(`${this.cameraName} Yeah a good segment!`);
const motionDetected = (_c = this.accessory
.getService(this.platform.Service.MotionSensor)) === null || _c === void 0 ? void 0 : _c.getCharacteristic(this.platform.Characteristic.MotionDetected).value;
yield {
data: segment,
isLast: !isMotionActive,
};
if (!isMotionActive) {
this.log.debug(this.cameraName, 'Ending recording session due to motion stopped!');
break;
}
}
}
catch (error) {
if (!this.handlingStreamingRequest && this.closeReason && this.closeReason === 3 /* HDSProtocolSpecificErrorReason.CANCELLED */) {
this.log.debug(this.cameraName, 'Recording encountered an error but that is expected, as the recording was canceled beforehand. Error: ' + error);
}
else {
this.log.error(this.cameraName, 'Error while recording: ' + error);
}
}
finally {
if (this.closeReason &&
this.closeReason !== 0 /* HDSProtocolSpecificErrorReason.NORMAL */ && this.closeReason !== 3 /* HDSProtocolSpecificErrorReason.CANCELLED */) {
this.log.warn(this.cameraName, `The recording process was aborted by HSV with reason "${HKSVQuitReason[this.closeReason]}"`);
}
if (this.closeReason && this.closeReason === 3 /* HDSProtocolSpecificErrorReason.CANCELLED */) {
this.log.debug(this.cameraName, 'The recording process was canceled by the HomeKit Controller."');
}
if (this.forceStopTimeout) {
clearTimeout(this.forceStopTimeout);
this.forceStopTimeout = undefined;
}
// check whether motion is still in progress
const motionDetected = (_d = this.accessory
.getService(this.platform.Service.MotionSensor)) === null || _d === void 0 ? void 0 : _d.getCharacteristic(this.platform.Characteristic.MotionDetected).value;
if (motionDetected) {
(_e = this.accessory
.getService(this.platform.Service.MotionSensor)) === null || _e === void 0 ? void 0 : _e.getCharacteristic(this.platform.Characteristic.MotionDetected).updateValue(false);
}
this.log.debug(this.cameraName, 'Streaming STOP! Recording!');
this.localLivestreamManager.stopLocalLiveStream();
}
}
updateRecordingActive(active) {
this.log.debug(`Recording: ${active}`, this.accessory.displayName);
}
updateRecordingConfiguration(configuration) {
this.configuration = configuration;
}
closeRecordingStream(streamId, reason) {
var _a, _b, _c;
this.log.info(this.cameraName, 'Closing recording process');
if (this.session) {
this.log.debug(this.cameraName, 'Stopping recording session.');
(_a = this.session.process) === null || _a === void 0 ? void 0 : _a.kill('SIGKILL');
this.session = undefined;
}
else {
this.log.warn('Recording session could not be closed gracefully.');
}
if (this.forceStopTimeout) {
clearTimeout(this.forceStopTimeout);
this.forceStopTimeout = undefined;
}
// check whether motion is still in progress
const motionDetected = (_b = this.accessory
.getService(this.platform.Service.MotionSensor)) === null || _b === void 0 ? void 0 : _b.getCharacteristic(this.platform.Characteristic.MotionDetected).value;
if (motionDetected) {
(_c = this.accessory
.getService(this.platform.Service.MotionSensor)) === null || _c === void 0 ? void 0 : _c.getCharacteristic(this.platform.Characteristic.MotionDetected).updateValue(false);
}
this.closeReason = reason;
this.handlingStreamingRequest = false;
}
acknowledgeStream(streamId) {
this.log.debug('end of recording acknowledged!');
this.closeRecordingStream(streamId, undefined);
}
}
exports.RecordingDelegate = RecordingDelegate;
//# sourceMappingURL=recordingDelegate.js.map