mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-08-10 04:41:11 +03:00
Miscellaneous fixes (0.18 beta) (#23934)
CI / AMD64 Build (push) Canceled after 0s
CI / ARM Build (push) Canceled after 0s
CI / Jetson Jetpack 6 (push) Canceled after 0s
CI / AMD64 Extra Build (push) Canceled after 0s
CI / ARM Extra Build (push) Canceled after 0s
CI / Synaptics Build (push) Canceled after 0s
CI / Assemble and push default build (push) Canceled after 0s
CI / AMD64 Build (push) Canceled after 0s
CI / ARM Build (push) Canceled after 0s
CI / Jetson Jetpack 6 (push) Canceled after 0s
CI / AMD64 Extra Build (push) Canceled after 0s
CI / ARM Extra Build (push) Canceled after 0s
CI / Synaptics Build (push) Canceled after 0s
CI / Assemble and push default build (push) Canceled after 0s
* add host npu requirements to docs * allow toggling live audio transcription via mqtt * improve spacing consistency on mobile drawers * fix clearing the region grid not surviving a restart
This commit is contained in:
@@ -256,7 +256,7 @@ The only field that is valid at the camera level is `enabled`.
|
||||
|
||||
#### Live transcription
|
||||
|
||||
The single camera Live view in the Frigate UI supports live transcription of audio for streams defined with the `audio` role. Use the Enable/Disable Live Audio Transcription button/switch to toggle transcription processing. When speech is heard, the UI will display a black box over the top of the camera stream with text. The MQTT topic `frigate/<camera_name>/audio/transcription` will also be updated in real-time with transcribed text.
|
||||
The single camera Live view in the Frigate UI supports live transcription of audio for streams defined with the `audio` role. Use the Enable/Disable Live Audio Transcription button/switch to toggle transcription processing, or toggle it outside of the UI with the [`frigate/<camera_name>/audio_transcription/set`](/integrations/mqtt#frigatecamera_nameaudio_transcriptionset) MQTT topic or the HTTP API. When speech is heard, the UI will display a black box over the top of the camera stream with text. The MQTT topic `frigate/<camera_name>/audio/transcription` will also be updated in real-time with transcribed text.
|
||||
|
||||
Results can be error-prone due to a number of factors, including:
|
||||
|
||||
|
||||
@@ -297,6 +297,14 @@ detectors:
|
||||
|
||||
:::
|
||||
|
||||
### Intel NPU host requirements {#intel-npu-requirements}
|
||||
|
||||
The NPU firmware is loaded by the host kernel and is not part of the Frigate image. Everything else the NPU needs is bundled in the container, so host NPU libraries should never be mounted in.
|
||||
|
||||
Frigate bundles a specific version of Intel's [linux-npu-driver](https://github.com/intel/linux-npu-driver/releases), and the host firmware must come from that release or a newer one. Firmware older than the bundled driver may fail with `MAPPED_INFERENCE_VERSION is NOT compatible with the ELF`, where `Expected` is the version the firmware supports and `received` is the version the bundled compiler produced. Distributions often package older firmware than the driver Frigate ships, so check the build date on the host with `sudo dmesg | grep -i vpu` and update it there if needed.
|
||||
|
||||
Intel NPUs cannot be used under Home Assistant OS, which does not include the NPU firmware.
|
||||
|
||||
### Configuration {#configuration-openvino}
|
||||
|
||||
<ModelConfigDropdown detectorTitle="OpenVINO" models={objectDetectorsModels.openvino.models} />
|
||||
|
||||
@@ -390,6 +390,18 @@ Topic to turn audio detection for a camera on and off. Expected values are `ON`
|
||||
|
||||
Topic with current state of audio detection for a camera. Published values are `ON` and `OFF`.
|
||||
|
||||
### `frigate/<camera_name>/audio_transcription/set`
|
||||
|
||||
Topic to turn [live audio transcription](/configuration/audio_detectors#live-transcription) for a camera on and off. Expected values are `ON` and `OFF`. Transcribed text is published to `frigate/<camera_name>/audio/transcription`.
|
||||
|
||||
`ON` is ignored unless audio transcription is enabled in the config for the camera. Unlike the other camera toggles, this one is not persisted across Frigate restarts.
|
||||
|
||||
**NOTE:** Requires audio detection and transcription to be enabled
|
||||
|
||||
### `frigate/<camera_name>/audio_transcription/state`
|
||||
|
||||
Topic with current state of live audio transcription for a camera. Published values are `ON` and `OFF`.
|
||||
|
||||
### `frigate/<camera_name>/recordings/set`
|
||||
|
||||
Topic to turn recordings for a camera on and off. Expected values are `ON` and `OFF`. The change is persisted across Frigate restarts (see [Runtime toggle persistence](/configuration/live#runtime-toggle-persistence)).
|
||||
|
||||
+16
-1
@@ -53,6 +53,7 @@ from frigate.util.file import (
|
||||
)
|
||||
from frigate.util.image import get_image_from_recording, get_image_quality_params
|
||||
from frigate.util.media import get_keyframe_before
|
||||
from frigate.util.object import create_empty_regions_grid
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -1083,7 +1084,21 @@ def clear_region_grid(request: Request, camera_name: str):
|
||||
status_code=404,
|
||||
)
|
||||
|
||||
Regions.delete().where(Regions.camera == camera_name).execute()
|
||||
# store an empty grid instead of deleting the row so the grid is
|
||||
# rebuilt from newly tracked objects and not from all past history
|
||||
region = {
|
||||
Regions.camera: camera_name,
|
||||
Regions.grid: create_empty_regions_grid(),
|
||||
Regions.last_update: datetime.now().timestamp(),
|
||||
}
|
||||
(
|
||||
Regions.insert(region)
|
||||
.on_conflict(
|
||||
conflict_target=[Regions.camera],
|
||||
update=region,
|
||||
)
|
||||
.execute()
|
||||
)
|
||||
return JSONResponse(
|
||||
content={"success": True, "message": "Region grid cleared"},
|
||||
)
|
||||
|
||||
@@ -77,6 +77,11 @@ class MqttClient(Communicator):
|
||||
"ON" if camera.audio.enabled_in_config else "OFF",
|
||||
retain=True,
|
||||
)
|
||||
self.publish(
|
||||
f"{camera_name}/audio_transcription/state",
|
||||
"ON" if camera.audio_transcription.live_enabled else "OFF",
|
||||
retain=True,
|
||||
)
|
||||
self.publish(
|
||||
f"{camera_name}/detect/state",
|
||||
"ON" if camera.detect.enabled else "OFF",
|
||||
@@ -258,6 +263,7 @@ class MqttClient(Communicator):
|
||||
"snapshots",
|
||||
"detect",
|
||||
"audio",
|
||||
"audio_transcription",
|
||||
"motion",
|
||||
"improve_contrast",
|
||||
"ptz_autotracker",
|
||||
|
||||
@@ -35,6 +35,11 @@ logger = logging.getLogger(__name__)
|
||||
GRID_SIZE = 8
|
||||
|
||||
|
||||
def create_empty_regions_grid() -> list[list[dict[str, Any]]]:
|
||||
"""Create a region grid with no learned sizes."""
|
||||
return [[{"sizes": []} for _ in range(GRID_SIZE)] for _ in range(GRID_SIZE)]
|
||||
|
||||
|
||||
def get_camera_regions_grid(
|
||||
name: str,
|
||||
detect: DetectConfig,
|
||||
@@ -47,12 +52,7 @@ def get_camera_regions_grid(
|
||||
grid = regions.grid
|
||||
last_update = regions.last_update
|
||||
except DoesNotExist:
|
||||
grid = []
|
||||
for x in range(GRID_SIZE):
|
||||
row = []
|
||||
for y in range(GRID_SIZE):
|
||||
row.append({"sizes": []})
|
||||
grid.append(row)
|
||||
grid = create_empty_regions_grid()
|
||||
last_update = 0
|
||||
|
||||
# get events for timeline entries
|
||||
|
||||
@@ -37,7 +37,7 @@ export function LogSettingsButton({
|
||||
</Button>
|
||||
);
|
||||
const content = (
|
||||
<div className={cn("my-3 space-y-3 py-3 md:mt-0 md:py-0")}>
|
||||
<div className={cn("my-3 space-y-3 px-3 py-3 md:mt-0 md:px-0 md:py-0")}>
|
||||
<div className="space-y-4">
|
||||
<div className="space-y-0.5">
|
||||
<div>{t("filter")}</div>
|
||||
@@ -77,7 +77,7 @@ export function LogSettingsButton({
|
||||
return (
|
||||
<Drawer>
|
||||
<DrawerTrigger asChild>{trigger}</DrawerTrigger>
|
||||
<DrawerContent className="mx-1 max-h-[75dvh] overflow-hidden p-3">
|
||||
<DrawerContent className="mx-1 max-h-[75dvh] overflow-hidden">
|
||||
{content}
|
||||
</DrawerContent>
|
||||
</Drawer>
|
||||
|
||||
@@ -26,6 +26,7 @@ import PlatformAwareDialog from "../overlay/dialog/PlatformAwareDialog";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { getTranslatedLabel } from "@/utils/i18n";
|
||||
import { useAllowedCameras } from "@/hooks/use-allowed-cameras";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const REVIEW_FILTERS = [
|
||||
"cameras",
|
||||
@@ -409,6 +410,7 @@ function GeneralFilterButton({
|
||||
onUpdateFilter(resetFilter);
|
||||
}}
|
||||
onClose={() => setOpen(false)}
|
||||
contentClassName="p-4"
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -416,6 +418,7 @@ function GeneralFilterButton({
|
||||
<PlatformAwareDialog
|
||||
trigger={trigger}
|
||||
content={content}
|
||||
contentClassName="p-1"
|
||||
open={open}
|
||||
onOpenChange={(open) => {
|
||||
if (!open) {
|
||||
@@ -444,6 +447,7 @@ type GeneralFilterContentProps = {
|
||||
onApply: () => void;
|
||||
onReset: () => void;
|
||||
onClose: () => void;
|
||||
contentClassName?: string;
|
||||
};
|
||||
export function GeneralFilterContent({
|
||||
allLabels,
|
||||
@@ -454,6 +458,7 @@ export function GeneralFilterContent({
|
||||
onApply,
|
||||
onReset,
|
||||
onClose,
|
||||
contentClassName,
|
||||
}: GeneralFilterContentProps) {
|
||||
const { t } = useTranslation(["components/filter", "views/events"]);
|
||||
const { data: config } = useSWR<FrigateConfig>("config", {
|
||||
@@ -476,7 +481,12 @@ export function GeneralFilterContent({
|
||||
}, [config]);
|
||||
return (
|
||||
<>
|
||||
<div className="scrollbar-container h-auto max-h-[80dvh] overflow-y-auto overflow-x-hidden">
|
||||
<div
|
||||
className={cn(
|
||||
"scrollbar-container h-auto max-h-[80dvh] overflow-y-auto overflow-x-hidden",
|
||||
contentClassName,
|
||||
)}
|
||||
>
|
||||
{currentSeverity && (
|
||||
<div className="my-2.5 flex flex-col gap-2.5">
|
||||
<FilterSwitch
|
||||
|
||||
@@ -8,6 +8,7 @@ import { Label } from "../ui/label";
|
||||
import { Switch } from "../ui/switch";
|
||||
import { DropdownMenuSeparator } from "../ui/dropdown-menu";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
type ZoneMaskFilterButtonProps = {
|
||||
selectedZoneMask?: PolygonType[];
|
||||
@@ -46,8 +47,12 @@ export function ZoneMaskFilterButton({
|
||||
return (
|
||||
<Drawer>
|
||||
<DrawerTrigger asChild>{trigger}</DrawerTrigger>
|
||||
<DrawerContent className="mx-1 max-h-[75dvh] overflow-hidden p-3">
|
||||
{content}
|
||||
<DrawerContent className="mx-1 max-h-[75dvh] overflow-hidden">
|
||||
<GeneralFilterContent
|
||||
selectedZoneMask={selectedZoneMask}
|
||||
updateZoneMaskFilter={updateZoneMaskFilter}
|
||||
contentClassName="p-3"
|
||||
/>
|
||||
</DrawerContent>
|
||||
</Drawer>
|
||||
);
|
||||
@@ -64,15 +69,22 @@ export function ZoneMaskFilterButton({
|
||||
type GeneralFilterContentProps = {
|
||||
selectedZoneMask: PolygonType[] | undefined;
|
||||
updateZoneMaskFilter: (labels: PolygonType[] | undefined) => void;
|
||||
contentClassName?: string;
|
||||
};
|
||||
export function GeneralFilterContent({
|
||||
selectedZoneMask,
|
||||
updateZoneMaskFilter,
|
||||
contentClassName,
|
||||
}: GeneralFilterContentProps) {
|
||||
const { t } = useTranslation(["components/filter"]);
|
||||
return (
|
||||
<>
|
||||
<div className="h-auto overflow-y-auto overflow-x-hidden">
|
||||
<div
|
||||
className={cn(
|
||||
"h-auto overflow-y-auto overflow-x-hidden",
|
||||
contentClassName,
|
||||
)}
|
||||
>
|
||||
<div className="my-2.5 flex items-center justify-between">
|
||||
<Label
|
||||
className="mx-2 cursor-pointer text-primary"
|
||||
|
||||
@@ -257,7 +257,7 @@ export default function GeneralSettings({
|
||||
className={
|
||||
isDesktop
|
||||
? "scrollbar-container mr-5 w-72 overflow-y-auto"
|
||||
: "max-h-[75dvh] overflow-hidden p-2"
|
||||
: "max-h-[75dvh] overflow-hidden"
|
||||
}
|
||||
>
|
||||
{!isDesktop && (
|
||||
@@ -270,7 +270,12 @@ export default function GeneralSettings({
|
||||
</DrawerDescription>
|
||||
</>
|
||||
)}
|
||||
<div className="scrollbar-container w-full flex-col overflow-y-auto overflow-x-hidden">
|
||||
<div
|
||||
className={cn(
|
||||
"scrollbar-container w-full flex-col overflow-y-auto overflow-x-hidden",
|
||||
!isDesktop && "p-2",
|
||||
)}
|
||||
>
|
||||
{isMobile && (
|
||||
<div className="mb-2">
|
||||
<DropdownMenuLabel>
|
||||
|
||||
@@ -174,11 +174,11 @@ function StatusAlertNav({ className, large }: StatusAlertNavProps) {
|
||||
</DrawerTrigger>
|
||||
<DrawerContent
|
||||
className={cn(
|
||||
"mx-1 max-h-[75dvh] overflow-hidden rounded-t-2xl px-2",
|
||||
"mx-1 max-h-[75dvh] overflow-hidden rounded-t-2xl",
|
||||
className,
|
||||
)}
|
||||
>
|
||||
<div className="scrollbar-container flex h-auto w-full flex-col items-center gap-2 overflow-y-auto overflow-x-hidden py-4">
|
||||
<div className="scrollbar-container flex h-auto w-full flex-col items-center gap-2 overflow-y-auto overflow-x-hidden px-2 py-4">
|
||||
{Object.entries(messages).map(([key, messageArray]) => (
|
||||
<div key={key} className="flex w-full items-center gap-2">
|
||||
{messageArray.map(({ id, text, color, link }: StatusMessage) => {
|
||||
|
||||
@@ -34,8 +34,8 @@ export default function MobileCameraDrawer({
|
||||
<FaVideo className="text-secondary-foreground" />
|
||||
</Button>
|
||||
</DrawerTrigger>
|
||||
<DrawerContent className="mx-1 max-h-[75dvh] overflow-hidden rounded-t-2xl px-4">
|
||||
<div className="scrollbar-container flex h-auto w-full flex-col items-center gap-2 overflow-y-auto overflow-x-hidden py-4">
|
||||
<DrawerContent className="mx-1 max-h-[75dvh] overflow-hidden rounded-t-2xl">
|
||||
<div className="scrollbar-container flex h-auto w-full flex-col items-center gap-2 overflow-y-auto overflow-x-hidden p-4">
|
||||
{allCameras.map((cam) => (
|
||||
<div
|
||||
key={cam}
|
||||
|
||||
@@ -30,6 +30,7 @@ import { useNavigate } from "react-router-dom";
|
||||
import { StartExportResponse } from "@/types/export";
|
||||
import { ShareTimestampContent } from "./ShareTimestampDialog";
|
||||
import { useIsAdmin } from "@/hooks/use-is-admin";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
type DrawerMode =
|
||||
| "none"
|
||||
@@ -518,9 +519,9 @@ export default function MobileReviewSettingsDrawer({
|
||||
} else if (drawerMode == "filter") {
|
||||
content = (
|
||||
<div className="scrollbar-container flex h-auto w-full flex-col overflow-y-auto overflow-x-hidden">
|
||||
<div className="relative mb-2 h-8 w-full">
|
||||
<div className="relative mb-4 h-8 w-full">
|
||||
<div
|
||||
className="absolute left-0 text-selected"
|
||||
className="absolute left-4 text-selected"
|
||||
onClick={() => setDrawerMode("select")}
|
||||
>
|
||||
{t("button.back", { ns: "common" })}
|
||||
@@ -548,6 +549,7 @@ export default function MobileReviewSettingsDrawer({
|
||||
onUpdateFilter(resetFilter);
|
||||
}}
|
||||
onClose={() => setDrawerMode("select")}
|
||||
contentClassName="px-4"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
@@ -685,7 +687,14 @@ export default function MobileReviewSettingsDrawer({
|
||||
</Button>
|
||||
</DrawerTrigger>
|
||||
<DrawerContent
|
||||
className={`mx-1 flex max-h-[80dvh] flex-col items-center gap-2 rounded-t-2xl px-4 pb-4 ${drawerMode == "export" || drawerMode == "debug-replay" ? "overflow-visible" : "overflow-hidden"}`}
|
||||
className={cn(
|
||||
"mx-1 flex max-h-[80dvh] flex-col items-center gap-2 rounded-t-2xl pb-4",
|
||||
// the filter content pads itself so its scrollbar reaches the drawer edge
|
||||
drawerMode != "filter" && "px-4",
|
||||
drawerMode == "export" || drawerMode == "debug-replay"
|
||||
? "overflow-visible"
|
||||
: "overflow-hidden",
|
||||
)}
|
||||
>
|
||||
{content}
|
||||
</DrawerContent>
|
||||
|
||||
@@ -491,7 +491,7 @@ export default function Step3StreamConfig({
|
||||
</Button>
|
||||
</div>
|
||||
</DrawerTrigger>
|
||||
<DrawerContent className="mx-1 max-h-[75dvh] overflow-hidden rounded-t-2xl px-2">
|
||||
<DrawerContent className="mx-1 max-h-[75dvh] overflow-hidden rounded-t-2xl">
|
||||
<div className="mt-2">
|
||||
<Command>
|
||||
<CommandInput
|
||||
@@ -500,7 +500,7 @@ export default function Step3StreamConfig({
|
||||
)}
|
||||
className="h-9"
|
||||
/>
|
||||
<CommandList>
|
||||
<CommandList className="px-2">
|
||||
<CommandEmpty>
|
||||
{t("cameraWizard.step3.noStreamFound")}
|
||||
</CommandEmpty>
|
||||
|
||||
@@ -47,7 +47,7 @@ const DrawerContent = React.forwardRef<
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<div className="mx-auto mt-4 h-2 w-[100px] rounded-full bg-muted" />
|
||||
<div className="mx-auto mt-4 h-2 w-[100px] shrink-0 rounded-full bg-muted" />
|
||||
{children}
|
||||
</DrawerPrimitive.Content>
|
||||
</DrawerPortal>
|
||||
|
||||
Reference in New Issue
Block a user