From 8198165fa7af695977a62e40f70149ed3eec48d8 Mon Sep 17 00:00:00 2001 From: Nick Mowen Date: Mon, 19 Jun 2023 15:44:21 -0600 Subject: [PATCH] Add audio icon in frontend --- frigate/config.py | 6 +++++- web/src/api/ws.jsx | 9 +++++++++ web/src/icons/Audio.jsx | 37 +++++++++++++++++++++++++++++++++++++ web/src/routes/Cameras.jsx | 35 +++++++++++++++++++++++++++-------- 4 files changed, 78 insertions(+), 9 deletions(-) create mode 100644 web/src/icons/Audio.jsx 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 ( - } /> + } + /> ); }