diff --git a/frigate/config.py b/frigate/config.py
index c86be26ac..b850332da 100644
--- a/frigate/config.py
+++ b/frigate/config.py
@@ -396,6 +396,9 @@ class AudioConfig(FrigateBaseModel):
listen: List[str] = Field(
default=DEFAULT_LISTEN_AUDIO, title="Audio to listen for."
)
+ enabled_in_config: Optional[bool] = Field(
+ title="Keep track of original state of audio detection."
+ )
class BirdseyeModeEnum(str, Enum):
@@ -1004,8 +1007,9 @@ class FrigateConfig(FrigateBaseModel):
camera_config.onvif.password = camera_config.onvif.password.format(
**FRIGATE_ENV_VARS
)
- # set config recording value
+ # set config pre-value
camera_config.record.enabled_in_config = camera_config.record.enabled
+ camera_config.audio.enabled_in_config = camera_config.audio.enabled
# Add default filters
object_keys = camera_config.objects.track
diff --git a/web/src/api/ws.jsx b/web/src/api/ws.jsx
index 0867ed0a4..6671bb260 100644
--- a/web/src/api/ws.jsx
+++ b/web/src/api/ws.jsx
@@ -120,6 +120,15 @@ export function useSnapshotsState(camera) {
return { payload, send, connected };
}
+export function useAudioState(camera) {
+ const {
+ value: { payload },
+ send,
+ connected,
+ } = useWs(`${camera}/audio/state`, `${camera}/audio/set`);
+ return { payload, send, connected };
+}
+
export function usePtzCommand(camera) {
const {
value: { payload },
diff --git a/web/src/icons/Audio.jsx b/web/src/icons/Audio.jsx
new file mode 100644
index 000000000..68944f70d
--- /dev/null
+++ b/web/src/icons/Audio.jsx
@@ -0,0 +1,37 @@
+import { h } from 'preact';
+import { memo } from 'preact/compat';
+
+export function Snapshot({ className = 'h-6 w-6', stroke = 'currentColor', onClick = () => {} }) {
+ return (
+
+ );
+}
+
+export default memo(Snapshot);
diff --git a/web/src/routes/Cameras.jsx b/web/src/routes/Cameras.jsx
index 1e2bbf903..35c90f644 100644
--- a/web/src/routes/Cameras.jsx
+++ b/web/src/routes/Cameras.jsx
@@ -2,10 +2,11 @@ import { h, Fragment } from 'preact';
import ActivityIndicator from '../components/ActivityIndicator';
import Card from '../components/Card';
import CameraImage from '../components/CameraImage';
+import AudioIcon from '../icons/Audio';
import ClipIcon from '../icons/Clip';
import MotionIcon from '../icons/Motion';
import SnapshotIcon from '../icons/Snapshot';
-import { useDetectState, useRecordingsState, useSnapshotsState } from '../api/ws';
+import { useAudioState, useDetectState, useRecordingsState, useSnapshotsState } from '../api/ws';
import { useMemo } from 'preact/hooks';
import useSWR from 'swr';
@@ -43,6 +44,7 @@ function Camera({ name, config }) {
const { payload: detectValue, send: sendDetect } = useDetectState(name);
const { payload: recordValue, send: sendRecordings } = useRecordingsState(name);
const { payload: snapshotValue, send: sendSnapshots } = useSnapshotsState(name);
+ const { payload: audioValue, send: sendAudio } = useAudioState(name);
const href = `/cameras/${name}`;
const buttons = useMemo(() => {
return [
@@ -50,10 +52,9 @@ function Camera({ name, config }) {
{ name: 'Recordings', href: `/recording/${name}` },
];
}, [name]);
- const cleanName = useMemo(
- () => { return `${name.replaceAll('_', ' ')}` },
- [name]
- );
+ const cleanName = useMemo(() => {
+ return `${name.replaceAll('_', ' ')}`;
+ }, [name]);
const icons = useMemo(
() => [
{
@@ -65,7 +66,9 @@ function Camera({ name, config }) {
},
},
{
- name: config.record.enabled_in_config ? `Toggle recordings ${recordValue === 'ON' ? 'off' : 'on'}` : 'Recordings must be enabled in the config to be turned on in the UI.',
+ name: config.record.enabled_in_config
+ ? `Toggle recordings ${recordValue === 'ON' ? 'off' : 'on'}`
+ : 'Recordings must be enabled in the config to be turned on in the UI.',
icon: ClipIcon,
color: config.record.enabled_in_config ? (recordValue === 'ON' ? 'blue' : 'gray') : 'red',
onClick: () => {
@@ -82,11 +85,27 @@ function Camera({ name, config }) {
sendSnapshots(snapshotValue === 'ON' ? 'OFF' : 'ON', true);
},
},
+ config.audio.enabled_in_config
+ ? {
+ name: `Toggle audio detection ${audioValue === 'ON' ? 'off' : 'on'}`,
+ icon: AudioIcon,
+ color: audioValue === 'ON' ? 'blue' : 'gray',
+ onClick: () => {
+ sendAudio(audioValue === 'ON' ? 'OFF' : 'ON', true);
+ },
+ }
+ : {},
],
- [config, detectValue, sendDetect, recordValue, sendRecordings, snapshotValue, sendSnapshots]
+ [config, audioValue, sendAudio, detectValue, sendDetect, recordValue, sendRecordings, snapshotValue, sendSnapshots]
);
return (
- } />
+ }
+ />
);
}