Skip to content

Commit 0a8f499

Browse files
authored
Miscellaneous fixes (0.17 beta) (#21683)
* misc triggers tweaks i18n fixes fix toaster color fix clicking on labels selecting incorrect checkbox * update copilot instructions * lpr docs tweaks * add retry params to gemini * i18n fix * ensure users only see recognized plates from accessible cameras in explore * ensure all zone filters are converted to pixels zone-level filters were never converted from percentage area to pixels. RuntimeFilterConfig was only applied to filters at the camera level, not zone.filters. Fixes #21694 * add test for percentage based zone filters * use export id for key instead of name * update gemini docs
1 parent cfeb866 commit 0a8f499

File tree

12 files changed

+105
-29
lines changed

12 files changed

+105
-29
lines changed

.github/copilot-instructions.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
1-
Never write strings in the frontend directly, always write to and reference the relevant translations file.
2-
Always conform new and refactored code to the existing coding style in the project.
1+
- For Frigate NVR, never write strings in the frontend directly. Since the project uses `react-i18next`, use `t()` and write the English string in the relevant translations file in `web/public/locales/en`.
2+
- Always conform new and refactored code to the existing coding style in the project.
3+
- Always have a way to test your work and confirm your changes. When running backend tests, use `python3 -u -m unittest`.

docs/docs/configuration/genai.md

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,6 @@ Some models are labeled as **hybrid** (capable of both thinking and instruct tas
6666
**Recommendation:**
6767
Always select the `-instruct` or documented instruct/tagged variant of any model you use in your Frigate configuration. If in doubt, refer to your model provider’s documentation or model library for guidance on the correct model variant to use.
6868

69-
70-
7169
### Supported Models
7270

7371
You must use a vision capable model with Frigate. Current model variants can be found [in their model library](https://ollama.com/search?c=vision). Note that Frigate will not automatically download the model you specify in your config, you must download the model to your local instance of Ollama first i.e. by running `ollama pull qwen3-vl:2b-instruct` on your Ollama server/Docker container. Note that the model specified in Frigate's config must match the downloaded model tag.
@@ -93,7 +91,7 @@ genai:
9391

9492
## Google Gemini
9593

96-
Google Gemini has a free tier allowing [15 queries per minute](https://ai.google.dev/pricing) to the API, which is more than sufficient for standard Frigate usage.
94+
Google Gemini has a [free tier](https://ai.google.dev/pricing) for the API, however the limits may not be sufficient for standard Frigate usage. Choose a plan appropriate for your installation.
9795

9896
### Supported Models
9997

@@ -114,7 +112,7 @@ To start using Gemini, you must first get an API key from [Google AI Studio](htt
114112
genai:
115113
provider: gemini
116114
api_key: "{FRIGATE_GEMINI_API_KEY}"
117-
model: gemini-2.0-flash
115+
model: gemini-2.5-flash
118116
```
119117

120118
:::note

docs/docs/configuration/license_plate_recognition.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,8 @@ Fine-tune the LPR feature using these optional parameters at the global level of
6868
- Default: `1000` pixels. Note: this is intentionally set very low as it is an _area_ measurement (length x width). For reference, 1000 pixels represents a ~32x32 pixel square in your camera image.
6969
- Depending on the resolution of your camera's `detect` stream, you can increase this value to ignore small or distant plates.
7070
- **`device`**: Device to use to run license plate detection _and_ recognition models.
71-
- Default: `CPU`
72-
- This can be `CPU`, `GPU`, or the GPU's device number. For users without a model that detects license plates natively, using a GPU may increase performance of the YOLOv9 license plate detector model. See the [Hardware Accelerated Enrichments](/configuration/hardware_acceleration_enrichments.md) documentation. However, for users who run a model that detects `license_plate` natively, there is little to no performance gain reported with running LPR on GPU compared to the CPU.
71+
- Default: `None`
72+
- This is auto-selected by Frigate and can be `CPU`, `GPU`, or the GPU's device number. For users without a model that detects license plates natively, using a GPU may increase performance of the YOLOv9 license plate detector model. See the [Hardware Accelerated Enrichments](/configuration/hardware_acceleration_enrichments.md) documentation. However, for users who run a model that detects `license_plate` natively, there is little to no performance gain reported with running LPR on GPU compared to the CPU.
7373
- **`model_size`**: The size of the model used to identify regions of text on plates.
7474
- Default: `small`
7575
- This can be `small` or `large`.
@@ -432,6 +432,6 @@ If you are using a model that natively detects `license_plate`, add an _object m
432432

433433
If you are not using a model that natively detects `license_plate` or you are using dedicated LPR camera mode, only a _motion mask_ over your text is required.
434434

435-
### I see "Error running ... model" in my logs. How can I fix this?
435+
### I see "Error running ... model" in my logs, or my inference time is very high. How can I fix this?
436436

437437
This usually happens when your GPU is unable to compile or use one of the LPR models. Set your `device` to `CPU` and try again. GPU acceleration only provides a slight performance increase, and the models are lightweight enough to run without issue on most CPUs.

frigate/api/app.py

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,12 @@
2323
from peewee import SQL, fn, operator
2424
from pydantic import ValidationError
2525

26-
from frigate.api.auth import allow_any_authenticated, allow_public, require_role
26+
from frigate.api.auth import (
27+
allow_any_authenticated,
28+
allow_public,
29+
get_allowed_cameras_for_filter,
30+
require_role,
31+
)
2732
from frigate.api.defs.query.app_query_parameters import AppTimelineHourlyQueryParameters
2833
from frigate.api.defs.request.app_body import AppConfigSetBody
2934
from frigate.api.defs.tags import Tags
@@ -687,13 +692,19 @@ def plusModels(request: Request, filterByCurrentModelDetector: bool = False):
687692
@router.get(
688693
"/recognized_license_plates", dependencies=[Depends(allow_any_authenticated())]
689694
)
690-
def get_recognized_license_plates(split_joined: Optional[int] = None):
695+
def get_recognized_license_plates(
696+
split_joined: Optional[int] = None,
697+
allowed_cameras: List[str] = Depends(get_allowed_cameras_for_filter),
698+
):
691699
try:
692700
query = (
693701
Event.select(
694702
SQL("json_extract(data, '$.recognized_license_plate') AS plate")
695703
)
696-
.where(SQL("json_extract(data, '$.recognized_license_plate') IS NOT NULL"))
704+
.where(
705+
(SQL("json_extract(data, '$.recognized_license_plate') IS NOT NULL"))
706+
& (Event.camera << allowed_cameras)
707+
)
697708
.distinct()
698709
)
699710
recognized_license_plates = [row[0] for row in query.tuples()]

frigate/config/config.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -662,6 +662,13 @@ def post_validation(self, info: ValidationInfo) -> Self:
662662
# generate zone contours
663663
if len(camera_config.zones) > 0:
664664
for zone in camera_config.zones.values():
665+
if zone.filters:
666+
for object_name, filter_config in zone.filters.items():
667+
zone.filters[object_name] = RuntimeFilterConfig(
668+
frame_shape=camera_config.frame_shape,
669+
**filter_config.model_dump(exclude_unset=True),
670+
)
671+
665672
zone.generate_contour(camera_config.frame_shape)
666673

667674
# Set live view stream if none is set

frigate/genai/gemini.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,14 @@ def _init_provider(self):
2424
http_options_dict = {
2525
"api_version": "v1",
2626
"timeout": int(self.timeout * 1000), # requires milliseconds
27+
"retry_options": types.HttpRetryOptions(
28+
attempts=3,
29+
initial_delay=1.0,
30+
max_delay=60.0,
31+
exp_base=2.0,
32+
jitter=1.0,
33+
http_status_codes=[429, 500, 502, 503, 504],
34+
),
2735
}
2836

2937
if isinstance(self.genai_config.provider_options, dict):

frigate/test/test_config.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -632,6 +632,49 @@ def test_zone_assigns_color_and_contour(self):
632632
)
633633
assert frigate_config.cameras["back"].zones["test"].color != (0, 0, 0)
634634

635+
def test_zone_filter_area_percent_converts_to_pixels(self):
636+
config = {
637+
"mqtt": {"host": "mqtt"},
638+
"record": {
639+
"alerts": {
640+
"retain": {
641+
"days": 20,
642+
}
643+
}
644+
},
645+
"cameras": {
646+
"back": {
647+
"ffmpeg": {
648+
"inputs": [
649+
{"path": "rtsp://10.0.0.1:554/video", "roles": ["detect"]}
650+
]
651+
},
652+
"detect": {
653+
"height": 1080,
654+
"width": 1920,
655+
"fps": 5,
656+
},
657+
"zones": {
658+
"notification": {
659+
"coordinates": "0.03,1,0.025,0,0.626,0,0.643,1",
660+
"objects": ["person"],
661+
"filters": {"person": {"min_area": 0.1}},
662+
}
663+
},
664+
}
665+
},
666+
}
667+
668+
frigate_config = FrigateConfig(**config)
669+
expected_min_area = int(1080 * 1920 * 0.1)
670+
assert (
671+
frigate_config.cameras["back"]
672+
.zones["notification"]
673+
.filters["person"]
674+
.min_area
675+
== expected_min_area
676+
)
677+
635678
def test_zone_relative_matches_explicit(self):
636679
config = {
637680
"mqtt": {"host": "mqtt"},

web/public/locales/en/common.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
"untilForTime": "Until {{time}}",
44
"untilForRestart": "Until Frigate restarts.",
55
"untilRestart": "Until restart",
6+
"never": "Never",
67
"ago": "{{timeAgo}} ago",
78
"justNow": "Just now",
89
"today": "Today",

web/src/components/overlay/CreateTriggerDialog.tsx

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,7 @@ export default function CreateTriggerDialog({
268268
<FormItem className="flex flex-row items-center justify-between">
269269
<div className="space-y-0.5">
270270
<FormLabel className="text-base">
271-
{t("enabled", { ns: "common" })}
271+
{t("button.enabled", { ns: "common" })}
272272
</FormLabel>
273273
<div className="text-sm text-muted-foreground">
274274
{t("triggers.dialog.form.enabled.description")}
@@ -394,7 +394,10 @@ export default function CreateTriggerDialog({
394394
</FormLabel>
395395
<div className="space-y-2">
396396
{availableActions.map((action) => (
397-
<div key={action} className="flex items-center space-x-2">
397+
<label
398+
key={action}
399+
className="flex cursor-pointer items-center space-x-2"
400+
>
398401
<FormControl>
399402
<Checkbox
400403
checked={form
@@ -416,10 +419,10 @@ export default function CreateTriggerDialog({
416419
}}
417420
/>
418421
</FormControl>
419-
<FormLabel className="text-sm font-normal">
422+
<span className="text-sm font-normal">
420423
{t(`triggers.actions.${action}`)}
421-
</FormLabel>
422-
</div>
424+
</span>
425+
</label>
423426
))}
424427
</div>
425428
<FormDescription>

web/src/components/trigger/wizard/Step3ThresholdAndActions.tsx

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,10 @@ export default function Step3ThresholdAndActions({
142142
<FormLabel>{t("triggers.dialog.form.actions.title")}</FormLabel>
143143
<div className="space-y-2">
144144
{availableActions.map((action) => (
145-
<div key={action} className="flex items-center space-x-2">
145+
<label
146+
key={action}
147+
className="flex cursor-pointer items-center space-x-2"
148+
>
146149
<FormControl>
147150
<Checkbox
148151
checked={form
@@ -164,10 +167,10 @@ export default function Step3ThresholdAndActions({
164167
}}
165168
/>
166169
</FormControl>
167-
<FormLabel className="text-sm font-normal">
170+
<span className="text-sm font-normal">
168171
{t(`triggers.actions.${action}`)}
169-
</FormLabel>
170-
</div>
172+
</span>
173+
</label>
171174
))}
172175
</div>
173176
<FormDescription>
@@ -197,9 +200,7 @@ export default function Step3ThresholdAndActions({
197200
{isLoading && <ActivityIndicator className="mr-2 size-5" />}
198201
{isLoading
199202
? t("button.saving", { ns: "common" })
200-
: t("triggers.dialog.form.save", {
201-
defaultValue: "Save Trigger",
202-
})}
203+
: t("button.save", { ns: "common" })}
203204
</Button>
204205
</div>
205206
</form>

0 commit comments

Comments
 (0)