Dynamically enable/disable cameras (#16894)

* config options

* metrics

* stop and restart ffmpeg processes

* dispatcher

* frontend websocket

* buttons for testing

* don't recreate log pipe

* add/remove cam from birdseye when enabling/disabling

* end all objects and send empty camera activity

* enable/disable switch in ui

* disable buttons when camera is disabled

* use enabled_in_config for some frontend checks

* tweaks

* handle settings pane with disabled cameras

* frontend tweaks

* change to debug log

* mqtt docs

* tweak

* ensure all ffmpeg processes are initially started

* clean up

* use zmq

* remove camera metrics

* remove camera metrics

* tweaks

* frontend tweaks
This commit is contained in:
Josh Hawkins
2025-03-03 08:30:52 -07:00
committed by GitHub
parent 71e6e04d77
commit 531042467a
24 changed files with 713 additions and 202 deletions
+31 -1
View File
@@ -2,6 +2,7 @@ import {
useAudioState,
useAutotrackingState,
useDetectState,
useEnabledState,
usePtzCommand,
useRecordingsState,
useSnapshotsState,
@@ -82,6 +83,8 @@ import {
LuHistory,
LuInfo,
LuPictureInPicture,
LuPower,
LuPowerOff,
LuVideo,
LuVideoOff,
LuX,
@@ -185,6 +188,10 @@ export default function LiveCameraView({
);
}, [cameraMetadata]);
// camera enabled state
const { payload: enabledState } = useEnabledState(camera.name);
const cameraEnabled = enabledState === "ON";
// click overlay for ptzs
const [clickOverlay, setClickOverlay] = useState(false);
@@ -470,6 +477,7 @@ export default function LiveCameraView({
setPip(false);
}
}}
disabled={!cameraEnabled}
/>
)}
{supports2WayTalk && (
@@ -481,11 +489,11 @@ export default function LiveCameraView({
title={`${mic ? "Disable" : "Enable"} Two Way Talk`}
onClick={() => {
setMic(!mic);
// Turn on audio when enabling the mic if audio is currently off
if (!mic && !audio) {
setAudio(true);
}
}}
disabled={!cameraEnabled}
/>
)}
{supportsAudioOutput && preferredLiveMode != "jsmpeg" && (
@@ -496,6 +504,7 @@ export default function LiveCameraView({
isActive={audio ?? false}
title={`${audio ? "Disable" : "Enable"} Camera Audio`}
onClick={() => setAudio(!audio)}
disabled={!cameraEnabled}
/>
)}
<FrigateCameraFeatures
@@ -517,6 +526,7 @@ export default function LiveCameraView({
setLowBandwidth={setLowBandwidth}
supportsAudioOutput={supportsAudioOutput}
supports2WayTalk={supports2WayTalk}
cameraEnabled={cameraEnabled}
/>
</div>
</TooltipProvider>
@@ -913,6 +923,7 @@ type FrigateCameraFeaturesProps = {
setLowBandwidth: React.Dispatch<React.SetStateAction<boolean>>;
supportsAudioOutput: boolean;
supports2WayTalk: boolean;
cameraEnabled: boolean;
};
function FrigateCameraFeatures({
camera,
@@ -931,10 +942,14 @@ function FrigateCameraFeatures({
setLowBandwidth,
supportsAudioOutput,
supports2WayTalk,
cameraEnabled,
}: FrigateCameraFeaturesProps) {
const { payload: detectState, send: sendDetect } = useDetectState(
camera.name,
);
const { payload: enabledState, send: sendEnabled } = useEnabledState(
camera.name,
);
const { payload: recordState, send: sendRecord } = useRecordingsState(
camera.name,
);
@@ -1043,6 +1058,15 @@ function FrigateCameraFeatures({
if (isDesktop || isTablet) {
return (
<>
<CameraFeatureToggle
className="p-2 md:p-0"
variant={fullscreen ? "overlay" : "primary"}
Icon={enabledState == "ON" ? LuPower : LuPowerOff}
isActive={enabledState == "ON"}
title={`${enabledState == "ON" ? "Disable" : "Enable"} Camera`}
onClick={() => sendEnabled(enabledState == "ON" ? "OFF" : "ON")}
disabled={false}
/>
<CameraFeatureToggle
className="p-2 md:p-0"
variant={fullscreen ? "overlay" : "primary"}
@@ -1050,6 +1074,7 @@ function FrigateCameraFeatures({
isActive={detectState == "ON"}
title={`${detectState == "ON" ? "Disable" : "Enable"} Detect`}
onClick={() => sendDetect(detectState == "ON" ? "OFF" : "ON")}
disabled={!cameraEnabled}
/>
<CameraFeatureToggle
className="p-2 md:p-0"
@@ -1058,6 +1083,7 @@ function FrigateCameraFeatures({
isActive={recordState == "ON"}
title={`${recordState == "ON" ? "Disable" : "Enable"} Recording`}
onClick={() => sendRecord(recordState == "ON" ? "OFF" : "ON")}
disabled={!cameraEnabled}
/>
<CameraFeatureToggle
className="p-2 md:p-0"
@@ -1066,6 +1092,7 @@ function FrigateCameraFeatures({
isActive={snapshotState == "ON"}
title={`${snapshotState == "ON" ? "Disable" : "Enable"} Snapshots`}
onClick={() => sendSnapshot(snapshotState == "ON" ? "OFF" : "ON")}
disabled={!cameraEnabled}
/>
{audioDetectEnabled && (
<CameraFeatureToggle
@@ -1075,6 +1102,7 @@ function FrigateCameraFeatures({
isActive={audioState == "ON"}
title={`${audioState == "ON" ? "Disable" : "Enable"} Audio Detect`}
onClick={() => sendAudio(audioState == "ON" ? "OFF" : "ON")}
disabled={!cameraEnabled}
/>
)}
{autotrackingEnabled && (
@@ -1087,6 +1115,7 @@ function FrigateCameraFeatures({
onClick={() =>
sendAutotracking(autotrackingState == "ON" ? "OFF" : "ON")
}
disabled={!cameraEnabled}
/>
)}
<CameraFeatureToggle
@@ -1099,6 +1128,7 @@ function FrigateCameraFeatures({
isActive={isRecording}
title={`${isRecording ? "Stop" : "Start"} on-demand recording`}
onClick={handleEventButtonClick}
disabled={!cameraEnabled}
/>
<DropdownMenu modal={false}>