Add local motion detector engine
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
import { io } from 'socket.io-client';
|
||||
|
||||
import { api } from './api';
|
||||
import { createMotionDetector } from './motion-detector';
|
||||
import { getAppState, patchAppState, resetAppState, setAppState } from './store';
|
||||
|
||||
const PAGE_PATHS = {
|
||||
@@ -79,6 +80,7 @@ let activeRecordingChunks = [];
|
||||
let activeRecordingStartedAt = null;
|
||||
let activeRecordingStreamSessionId = null;
|
||||
let lastMotionEventId = null;
|
||||
let motionDetector = null;
|
||||
|
||||
let cameraVideoElement = null;
|
||||
let clientVideoElement = null;
|
||||
@@ -357,6 +359,7 @@ const attachCameraStreamToElement = () => {
|
||||
if (localCameraStream) {
|
||||
void cameraVideoElement.play().catch(() => {});
|
||||
}
|
||||
applyMotionDetectionReadiness();
|
||||
};
|
||||
|
||||
const attachClientStreamToElement = () => {
|
||||
@@ -466,6 +469,78 @@ const stopCameraPreview = () => {
|
||||
cameraVideoElement.srcObject = null;
|
||||
}
|
||||
setAppState({ cameraPreviewReady: false });
|
||||
applyMotionDetectionReadiness();
|
||||
};
|
||||
|
||||
const updateMotionDetectionRuntime = (updates) => {
|
||||
patchAppState((state) => ({
|
||||
motionDetection: {
|
||||
...state.motionDetection,
|
||||
...updates
|
||||
}
|
||||
}));
|
||||
};
|
||||
|
||||
const ensureMotionDetector = () => {
|
||||
if (motionDetector) {
|
||||
return motionDetector;
|
||||
}
|
||||
|
||||
motionDetector = createMotionDetector({
|
||||
getSourceElement: () => cameraVideoElement,
|
||||
getConfig: () => getAppState().motionDetection,
|
||||
onUpdate: ({ state, score, activeMotion, activeSince }) => {
|
||||
const current = getAppState().motionDetection;
|
||||
const nextState = state === 'cooldown' && !activeMotion ? 'monitoring' : state;
|
||||
updateMotionDetectionRuntime({
|
||||
state: nextState,
|
||||
score,
|
||||
lastTriggeredAt: activeMotion
|
||||
? current.lastTriggeredAt ?? (activeSince ? new Date(activeSince).toISOString() : new Date().toISOString())
|
||||
: current.lastTriggeredAt
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return motionDetector;
|
||||
};
|
||||
|
||||
const shouldRunMotionDetector = () => {
|
||||
if (typeof document !== 'undefined' && document.visibilityState !== 'visible') {
|
||||
return false;
|
||||
}
|
||||
|
||||
const state = getAppState();
|
||||
return Boolean(
|
||||
state.device?.role === 'camera' &&
|
||||
state.motionDetection?.enabled &&
|
||||
state.cameraPreviewReady &&
|
||||
localCameraStream &&
|
||||
cameraVideoElement
|
||||
);
|
||||
};
|
||||
|
||||
const applyMotionDetectionReadiness = () => {
|
||||
const detector = ensureMotionDetector();
|
||||
if (shouldRunMotionDetector()) {
|
||||
if (!detector.isRunning()) {
|
||||
detector.start();
|
||||
addActivity('Motion Detection', 'Detector monitoring started');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (detector.isRunning()) {
|
||||
detector.stop('idle');
|
||||
addActivity('Motion Detection', 'Detector monitoring paused');
|
||||
return;
|
||||
}
|
||||
|
||||
updateMotionDetectionRuntime({ state: 'idle', score: 0 });
|
||||
};
|
||||
|
||||
const onVisibilityChange = () => {
|
||||
applyMotionDetectionReadiness();
|
||||
};
|
||||
|
||||
const clearClientStream = () => {
|
||||
@@ -1049,6 +1124,7 @@ const connectSocket = () => {
|
||||
if (getAppState().device?.role === 'camera') {
|
||||
void startCameraPreview();
|
||||
}
|
||||
applyMotionDetectionReadiness();
|
||||
});
|
||||
|
||||
socket.on('disconnect', () => {
|
||||
@@ -1056,6 +1132,7 @@ const connectSocket = () => {
|
||||
void stopLocalRecording();
|
||||
teardownPeerConnection();
|
||||
setAppState({ activeCameraDeviceId: null, activeStreamSessionId: null });
|
||||
applyMotionDetectionReadiness();
|
||||
});
|
||||
|
||||
socket.on('command:received', async (payload) => {
|
||||
@@ -1281,6 +1358,9 @@ const cleanupConnectionState = async () => {
|
||||
await stopLocalRecording();
|
||||
teardownPeerConnection();
|
||||
stopCameraPreview();
|
||||
if (motionDetector?.isRunning()) {
|
||||
motionDetector.stop('idle');
|
||||
}
|
||||
if (socket) {
|
||||
socket.disconnect();
|
||||
socket = null;
|
||||
@@ -1327,6 +1407,9 @@ const init = async () => {
|
||||
if (navigator.mediaDevices?.addEventListener) {
|
||||
navigator.mediaDevices.addEventListener('devicechange', onMediaDeviceChange);
|
||||
}
|
||||
if (typeof document !== 'undefined') {
|
||||
document.addEventListener('visibilitychange', onVisibilityChange);
|
||||
}
|
||||
|
||||
const saved = localStorage.getItem('mobileSimDevice');
|
||||
if (saved) {
|
||||
@@ -1365,6 +1448,7 @@ const init = async () => {
|
||||
|
||||
enforceRouteForSession();
|
||||
void refreshCameraInputDevices();
|
||||
applyMotionDetectionReadiness();
|
||||
|
||||
window.addEventListener('beforeunload', () => {
|
||||
void cleanupConnectionState();
|
||||
@@ -1383,6 +1467,9 @@ const destroy = async () => {
|
||||
if (navigator.mediaDevices?.removeEventListener) {
|
||||
navigator.mediaDevices.removeEventListener('devicechange', onMediaDeviceChange);
|
||||
}
|
||||
if (typeof document !== 'undefined') {
|
||||
document.removeEventListener('visibilitychange', onVisibilityChange);
|
||||
}
|
||||
initialized = false;
|
||||
await cleanupConnectionState();
|
||||
};
|
||||
@@ -1585,6 +1672,7 @@ const actions = {
|
||||
const motionDetection = updateMotionDetectionState({ enabled: Boolean(enabled) });
|
||||
pushToast(motionDetection.enabled ? 'Automatic detection armed' : 'Automatic detection paused', 'info');
|
||||
addActivity('Motion Detection', motionDetection.enabled ? 'Detector armed' : 'Detector paused');
|
||||
applyMotionDetectionReadiness();
|
||||
},
|
||||
|
||||
setMotionDetectionProfile(profile) {
|
||||
@@ -1592,6 +1680,7 @@ const actions = {
|
||||
const motionDetection = updateMotionDetectionState({ profile: nextProfile.profile });
|
||||
pushToast(`${nextProfile.label} profile selected`, 'success');
|
||||
addActivity('Motion Detection', `Profile set to ${motionDetection.profile}`);
|
||||
applyMotionDetectionReadiness();
|
||||
},
|
||||
|
||||
setMotionDetectionDebug(debug) {
|
||||
|
||||
Reference in New Issue
Block a user