Fix camera feed freezing and person detection alerts
- Add WebRTC auto-reconnect with exponential backoff when streams disconnect or fail, preventing permanent freezes in grid view - Replace hard-coded Front Porch person alert with generic system that monitors all configured personDetectionEntities - Map Frigate person_occupancy entities to cameras dynamically - Show correct camera name and feed in alert overlay - Bump config version to refresh detection entity defaults
This commit is contained in:
67
src/App.tsx
67
src/App.tsx
@@ -19,17 +19,26 @@ import { useUIStore, useCameraOverlay } from '@/stores/uiStore';
|
||||
import { useSettingsStore } from '@/stores/settingsStore';
|
||||
import { env } from '@/config/environment';
|
||||
|
||||
// Front porch alert overlay - shows for 30 seconds when person detected
|
||||
function FrontPorchAlert({ onClose }: { onClose: () => void }) {
|
||||
// Map a Frigate person_occupancy entity to its camera name
|
||||
// e.g. 'binary_sensor.fpe_person_occupancy' -> 'fpe' -> match camera by frigateCamera
|
||||
function entityToCameraName(entityId: string): string | null {
|
||||
const match = entityId.match(/^binary_sensor\.(.+)_person_occupancy$/);
|
||||
return match ? match[1] : null;
|
||||
}
|
||||
|
||||
// Person detection alert overlay - shows for 30 seconds when person detected on any configured camera
|
||||
function PersonAlert({ cameraName, onClose }: { cameraName: string; onClose: () => void }) {
|
||||
const cameras = useSettingsStore((state) => state.config.cameras);
|
||||
const frontPorchCamera = cameras.find((c) => c.name === 'Front_Porch');
|
||||
const camera = cameras.find(
|
||||
(c) => c.frigateCamera?.toLowerCase() === cameraName || c.name.toLowerCase() === cameraName,
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const timer = setTimeout(onClose, 30000); // 30 seconds
|
||||
const timer = setTimeout(onClose, 30000);
|
||||
return () => clearTimeout(timer);
|
||||
}, [onClose]);
|
||||
|
||||
if (!frontPorchCamera) return null;
|
||||
if (!camera) return null;
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 z-50 bg-black flex flex-col">
|
||||
@@ -37,7 +46,7 @@ function FrontPorchAlert({ onClose }: { onClose: () => void }) {
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-3 h-3 rounded-full bg-status-warning animate-pulse" />
|
||||
<h2 className="text-lg font-semibold text-status-warning">
|
||||
Person Detected - Front Porch
|
||||
Person Detected - {camera.displayName}
|
||||
</h2>
|
||||
</div>
|
||||
<button
|
||||
@@ -51,7 +60,7 @@ function FrontPorchAlert({ onClose }: { onClose: () => void }) {
|
||||
</div>
|
||||
<div className="flex-1 p-2">
|
||||
<CameraFeed
|
||||
camera={frontPorchCamera}
|
||||
camera={camera}
|
||||
className="w-full h-full"
|
||||
showLabel={false}
|
||||
/>
|
||||
@@ -174,9 +183,10 @@ export default function App() {
|
||||
const mediaOverlayOpen = useUIStore((state) => state.mediaOverlayOpen);
|
||||
const { isOpen: cameraOverlayOpen } = useCameraOverlay();
|
||||
|
||||
// Front porch alert state
|
||||
const [showFrontPorchAlert, setShowFrontPorchAlert] = useState(false);
|
||||
const frontPorchAlertShownRef = useRef(false);
|
||||
// Person detection alert state
|
||||
const personDetectionEntities = useSettingsStore((state) => state.config.personDetectionEntities);
|
||||
const [alertCamera, setAlertCamera] = useState<string | null>(null);
|
||||
const alertShownForRef = useRef<Set<string>>(new Set());
|
||||
|
||||
// Motion detection now runs in the Electron main process (MotionDetector.ts)
|
||||
// This prevents browser throttling when the screensaver is active
|
||||
@@ -230,29 +240,32 @@ export default function App() {
|
||||
initConfig();
|
||||
}, [accessToken, connect]);
|
||||
|
||||
// Listen for Front Porch person detection - show full screen overlay for 30 seconds
|
||||
// Listen for person detection on all configured entities
|
||||
useEffect(() => {
|
||||
if (!isConnected) return;
|
||||
|
||||
const frontPorchEntity = entities['binary_sensor.front_porch_person_occupancy'];
|
||||
const isPersonDetected = frontPorchEntity?.state === 'on';
|
||||
for (const entityId of personDetectionEntities) {
|
||||
const entity = entities[entityId];
|
||||
const isDetected = entity?.state === 'on';
|
||||
const cameraName = entityToCameraName(entityId);
|
||||
|
||||
if (isPersonDetected && !frontPorchAlertShownRef.current) {
|
||||
// Person just detected - show alert
|
||||
frontPorchAlertShownRef.current = true;
|
||||
setShowFrontPorchAlert(true);
|
||||
// Also wake the screen
|
||||
if (window.electronAPI?.screen?.wake) {
|
||||
window.electronAPI.screen.wake();
|
||||
if (isDetected && cameraName && !alertShownForRef.current.has(entityId)) {
|
||||
// Person just detected on this camera - show alert
|
||||
alertShownForRef.current.add(entityId);
|
||||
setAlertCamera(cameraName);
|
||||
if (window.electronAPI?.screen?.wake) {
|
||||
window.electronAPI.screen.wake();
|
||||
}
|
||||
break; // Show one alert at a time
|
||||
} else if (!isDetected) {
|
||||
// Reset so next detection on this entity triggers again
|
||||
alertShownForRef.current.delete(entityId);
|
||||
}
|
||||
} else if (!isPersonDetected) {
|
||||
// Reset flag when person clears so next detection triggers alert
|
||||
frontPorchAlertShownRef.current = false;
|
||||
}
|
||||
}, [isConnected, entities]);
|
||||
}, [isConnected, entities, personDetectionEntities]);
|
||||
|
||||
const closeFrontPorchAlert = useCallback(() => {
|
||||
setShowFrontPorchAlert(false);
|
||||
const closePersonAlert = useCallback(() => {
|
||||
setAlertCamera(null);
|
||||
}, []);
|
||||
|
||||
// Set up screen idle timeout
|
||||
@@ -321,7 +334,7 @@ export default function App() {
|
||||
{mediaOverlayOpen && <JellyfinOverlay />}
|
||||
{cameraOverlayOpen && <CameraOverlay />}
|
||||
{settingsOpen && <SettingsPanel />}
|
||||
{showFrontPorchAlert && <FrontPorchAlert onClose={closeFrontPorchAlert} />}
|
||||
{alertCamera && <PersonAlert cameraName={alertCamera} onClose={closePersonAlert} />}
|
||||
<GlobalKeyboard />
|
||||
</>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user