handle dedicated lpr mode

This commit is contained in:
Josh Hawkins 2026-05-19 08:22:35 -05:00
parent a53d4654b2
commit bc2f1cbcc2
4 changed files with 35 additions and 4 deletions

View File

@ -1,4 +1,5 @@
import type { HiddenFieldContext } from "@/types/configForm";
import { getEffectiveAttributeLabels } from "@/utils/configUtil";
import type { SectionConfigOverrides } from "./types";
// Attribute labels (face, license_plate, Frigate+ couriers like DHL/Amazon,
@ -27,7 +28,7 @@ const hideAttributeFilters = ({
fullConfig.objects?.track ??
[];
return (fullConfig.model?.all_attributes ?? [])
return getEffectiveAttributeLabels(fullConfig, fullCameraConfig, level)
.filter((attr) => !track.includes(attr))
.map((attr) => `filters.${attr}`);
};

View File

@ -10,6 +10,7 @@ import { RJSFSchema } from "@rjsf/utils";
import { applySchemaDefaults } from "@/lib/config-schema";
import { isJsonObject } from "@/lib/utils";
import { HiddenFieldContext, JsonObject, JsonValue } from "@/types/configForm";
import { getEffectiveAttributeLabels } from "@/utils/configUtil";
/**
* Sections that require special handling at the global level.
@ -149,7 +150,11 @@ function modifyObjectsSchema(
): RJSFSchema {
if (!ctx) return schema;
const allAttributes = ctx.fullConfig.model?.all_attributes ?? [];
const allAttributes = getEffectiveAttributeLabels(
ctx.fullConfig,
ctx.fullCameraConfig,
ctx.level,
);
// Resolve effective track at this scope, falling back through camera
// config then global config (matches hideAttributeFilters in objects.ts).

View File

@ -6,6 +6,7 @@
*/
import type { ConfigFormContext } from "@/types/configForm";
import { getEffectiveAttributeLabels } from "@/utils/configUtil";
const isRecord = (value: unknown): value is Record<string, unknown> =>
typeof value === "object" && value !== null;
@ -80,7 +81,11 @@ export function buildTranslationPath(
const filtersIndex = stringSegments.indexOf("filters");
if (filtersIndex !== -1 && stringSegments.length > filtersIndex + 2) {
const filterKey = stringSegments[filtersIndex + 1];
const allAttributes = formContext?.fullConfig?.model?.all_attributes ?? [];
const allAttributes = getEffectiveAttributeLabels(
formContext?.fullConfig,
formContext?.fullCameraConfig,
formContext?.level,
);
const sectionWord = allAttributes.includes(filterKey)
? "filters_attribute"
: "filters";

View File

@ -19,7 +19,7 @@ import {
sanitizeOverridesForSection,
} from "@/components/config-form/sections/section-special-cases";
import type { RJSFSchema } from "@rjsf/utils";
import type { FrigateConfig } from "@/types/frigateConfig";
import type { CameraConfig, FrigateConfig } from "@/types/frigateConfig";
import type {
ConfigSectionData,
HiddenFieldContext,
@ -749,6 +749,26 @@ export function getSectionConfig(
return mergeSectionConfig(entry.base, overrides);
}
/**
* Resolve the effective attribute label set at a given scope. At camera
* (and replay) scope on a dedicated LPR camera (`camera.type === "lpr"`),
* `license_plate` is treated as a regular tracked object not an
* attribute to match the backend's per-camera carve-out in
* `frigate/video/detect.py`. Returns the full attribute list at global
* scope and for non-LPR cameras.
*/
export function getEffectiveAttributeLabels(
fullConfig: FrigateConfig | undefined,
fullCameraConfig: CameraConfig | undefined,
level: "global" | "camera" | "replay" | undefined,
): string[] {
const all = fullConfig?.model?.all_attributes ?? [];
if (level !== "global" && fullCameraConfig?.type === "lpr") {
return all.filter((attr) => attr !== "license_plate");
}
return all;
}
/**
* Build a `HiddenFieldContext` for the common case where a callsite has
* `config`, an optional `cameraName`, and a level, but no per-section