mirror of
https://github.com/blakeblackshear/frigate.git
synced 2025-12-06 13:34:13 +03:00
Trigger actions (#20709)
Some checks are pending
CI / AMD64 Build (push) Waiting to run
CI / ARM Build (push) Waiting to run
CI / Jetson Jetpack 6 (push) Waiting to run
CI / AMD64 Extra Build (push) Blocked by required conditions
CI / ARM Extra Build (push) Blocked by required conditions
CI / Synaptics Build (push) Blocked by required conditions
CI / Assemble and push default build (push) Blocked by required conditions
Some checks are pending
CI / AMD64 Build (push) Waiting to run
CI / ARM Build (push) Waiting to run
CI / Jetson Jetpack 6 (push) Waiting to run
CI / AMD64 Extra Build (push) Blocked by required conditions
CI / ARM Extra Build (push) Blocked by required conditions
CI / Synaptics Build (push) Blocked by required conditions
CI / Assemble and push default build (push) Blocked by required conditions
* add backend trigger actions * config * frontend types * add actions to form and wizard * i18n * docs * use camera level notification enabled check
This commit is contained in:
parent
6ccf8cd2b8
commit
576f692dae
@ -924,10 +924,13 @@ cameras:
|
||||
type: thumbnail
|
||||
# Reference data for matching, either an event ID for `thumbnail` or a text string for `description`. (default: none)
|
||||
data: 1751565549.853251-b69j73
|
||||
# Similarity threshold for triggering. (default: none)
|
||||
threshold: 0.7
|
||||
# Similarity threshold for triggering. (default: shown below)
|
||||
threshold: 0.8
|
||||
# List of actions to perform when the trigger fires. (default: none)
|
||||
# Available options: `notification` (send a webpush notification)
|
||||
# Available options:
|
||||
# - `notification` (send a webpush notification)
|
||||
# - `sub_label` (add trigger friendly name as a sub label to the triggering tracked object)
|
||||
# - `attribute` (add trigger's name and similarity score as a data attribute to the triggering tracked object)
|
||||
actions:
|
||||
- notification
|
||||
|
||||
|
||||
@ -119,7 +119,7 @@ Semantic Search must be enabled to use Triggers.
|
||||
|
||||
### Configuration
|
||||
|
||||
Triggers are defined within the `semantic_search` configuration for each camera in your Frigate configuration file or through the UI. Each trigger consists of a `friendly_name`, a `type` (either `thumbnail` or `description`), a `data` field (the reference image event ID or text), a `threshold` for similarity matching, and a list of `actions` to perform when the trigger fires.
|
||||
Triggers are defined within the `semantic_search` configuration for each camera in your Frigate configuration file or through the UI. Each trigger consists of a `friendly_name`, a `type` (either `thumbnail` or `description`), a `data` field (the reference image event ID or text), a `threshold` for similarity matching, and a list of `actions` to perform when the trigger fires - `notification`, `sub_label`, and `attribute`.
|
||||
|
||||
Triggers are best configured through the Frigate UI.
|
||||
|
||||
@ -128,17 +128,20 @@ Triggers are best configured through the Frigate UI.
|
||||
1. Navigate to the **Settings** page and select the **Triggers** tab.
|
||||
2. Choose a camera from the dropdown menu to view or manage its triggers.
|
||||
3. Click **Add Trigger** to create a new trigger or use the pencil icon to edit an existing one.
|
||||
4. In the **Create Trigger** dialog:
|
||||
- Enter a **Name** for the trigger (e.g., "red_car_alert").
|
||||
4. In the **Create Trigger** wizard:
|
||||
- Enter a **Name** for the trigger (e.g., "Red Car Alert").
|
||||
- Enter a descriptive **Friendly Name** for the trigger (e.g., "Red car on the driveway camera").
|
||||
- Select the **Type** (`Thumbnail` or `Description`).
|
||||
- For `Thumbnail`, select an image to trigger this action when a similar thumbnail image is detected, based on the threshold.
|
||||
- For `Description`, enter text to trigger this action when a similar tracked object description is detected.
|
||||
- Set the **Threshold** for similarity matching.
|
||||
- Select **Actions** to perform when the trigger fires.
|
||||
If native webpush notifications are enabled, check the `Send Notification` box to send a notification.
|
||||
Check the `Add Sub Label` box to add the trigger's friendly name as a sub label to any triggering tracked objects.
|
||||
Check the `Add Attribute` box to add the trigger's internal ID (e.g., "red_car_alert") to a data attribute on the tracked object that can be processed via the API or MQTT.
|
||||
5. Save the trigger to update the configuration and store the embedding in the database.
|
||||
|
||||
When a trigger fires, the UI highlights the trigger with a blue outline for 3 seconds for easy identification.
|
||||
When a trigger fires, the UI highlights the trigger with a blue dot for 3 seconds for easy identification.
|
||||
|
||||
### Usage and Best Practices
|
||||
|
||||
|
||||
@ -33,6 +33,8 @@ class TriggerType(str, Enum):
|
||||
|
||||
class TriggerAction(str, Enum):
|
||||
NOTIFICATION = "notification"
|
||||
SUB_LABEL = "sub_label"
|
||||
ATTRIBUTE = "attribute"
|
||||
|
||||
|
||||
class ObjectClassificationType(str, Enum):
|
||||
|
||||
@ -10,6 +10,10 @@ import cv2
|
||||
import numpy as np
|
||||
from peewee import DoesNotExist
|
||||
|
||||
from frigate.comms.event_metadata_updater import (
|
||||
EventMetadataPublisher,
|
||||
EventMetadataTypeEnum,
|
||||
)
|
||||
from frigate.comms.inter_process import InterProcessRequestor
|
||||
from frigate.config import FrigateConfig
|
||||
from frigate.const import CONFIG_DIR
|
||||
@ -34,6 +38,7 @@ class SemanticTriggerProcessor(PostProcessorApi):
|
||||
db: SqliteVecQueueDatabase,
|
||||
config: FrigateConfig,
|
||||
requestor: InterProcessRequestor,
|
||||
sub_label_publisher: EventMetadataPublisher,
|
||||
metrics: DataProcessorMetrics,
|
||||
embeddings,
|
||||
):
|
||||
@ -41,6 +46,7 @@ class SemanticTriggerProcessor(PostProcessorApi):
|
||||
self.db = db
|
||||
self.embeddings = embeddings
|
||||
self.requestor = requestor
|
||||
self.sub_label_publisher = sub_label_publisher
|
||||
self.trigger_embeddings: list[np.ndarray] = []
|
||||
|
||||
self.thumb_stats = ZScoreNormalization()
|
||||
@ -184,14 +190,44 @@ class SemanticTriggerProcessor(PostProcessorApi):
|
||||
),
|
||||
)
|
||||
|
||||
friendly_name = (
|
||||
self.config.cameras[camera]
|
||||
.semantic_search.triggers[trigger["name"]]
|
||||
.friendly_name
|
||||
)
|
||||
|
||||
if (
|
||||
self.config.cameras[camera]
|
||||
.semantic_search.triggers[trigger["name"]]
|
||||
.actions
|
||||
):
|
||||
# TODO: handle actions for the trigger
|
||||
# handle actions for the trigger
|
||||
# notifications already handled by webpush
|
||||
pass
|
||||
if (
|
||||
"sub_label"
|
||||
in self.config.cameras[camera]
|
||||
.semantic_search.triggers[trigger["name"]]
|
||||
.actions
|
||||
):
|
||||
self.sub_label_publisher.publish(
|
||||
(event_id, friendly_name, similarity),
|
||||
EventMetadataTypeEnum.sub_label,
|
||||
)
|
||||
if (
|
||||
"attribute"
|
||||
in self.config.cameras[camera]
|
||||
.semantic_search.triggers[trigger["name"]]
|
||||
.actions
|
||||
):
|
||||
self.sub_label_publisher.publish(
|
||||
(
|
||||
event_id,
|
||||
trigger["name"],
|
||||
trigger["type"],
|
||||
similarity,
|
||||
),
|
||||
EventMetadataTypeEnum.attribute.value,
|
||||
)
|
||||
|
||||
if WRITE_DEBUG_IMAGES:
|
||||
try:
|
||||
|
||||
@ -233,6 +233,7 @@ class EmbeddingMaintainer(threading.Thread):
|
||||
db,
|
||||
self.config,
|
||||
self.requestor,
|
||||
self.event_metadata_publisher,
|
||||
metrics,
|
||||
self.embeddings,
|
||||
)
|
||||
|
||||
@ -903,8 +903,9 @@
|
||||
"description": "Description"
|
||||
},
|
||||
"actions": {
|
||||
"alert": "Mark as Alert",
|
||||
"notification": "Send Notification"
|
||||
"notification": "Send Notification",
|
||||
"sub_label": "Add Sub Label",
|
||||
"attribute": "Add Attribute"
|
||||
},
|
||||
"dialog": {
|
||||
"createTrigger": {
|
||||
@ -959,7 +960,7 @@
|
||||
},
|
||||
"actions": {
|
||||
"title": "Actions",
|
||||
"desc": "By default, Frigate fires an MQTT message for all triggers. Choose an additional action to perform when this trigger fires.",
|
||||
"desc": "By default, Frigate fires an MQTT message for all triggers. Sub labels add the trigger name to the object label. Attributes are searchable metadata stored separately in the tracked object metadata.",
|
||||
"error": {
|
||||
"min": "At least one action must be selected."
|
||||
}
|
||||
|
||||
@ -79,6 +79,15 @@ export default function CreateTriggerDialog({
|
||||
const { t } = useTranslation("views/settings");
|
||||
const { data: config } = useSWR<FrigateConfig>("config");
|
||||
|
||||
const availableActions = useMemo(() => {
|
||||
if (!config) return [];
|
||||
|
||||
if (config.cameras[selectedCamera].notifications.enabled_in_config) {
|
||||
return ["notification", "sub_label", "attribute"];
|
||||
}
|
||||
return ["sub_label", "attribute"];
|
||||
}, [config, selectedCamera]);
|
||||
|
||||
const existingTriggerNames = useMemo(() => {
|
||||
if (
|
||||
!config ||
|
||||
@ -132,7 +141,7 @@ export default function CreateTriggerDialog({
|
||||
.number()
|
||||
.min(0, t("triggers.dialog.form.threshold.error.min"))
|
||||
.max(1, t("triggers.dialog.form.threshold.error.max")),
|
||||
actions: z.array(z.enum(["notification"])),
|
||||
actions: z.array(z.enum(["notification", "sub_label", "attribute"])),
|
||||
});
|
||||
|
||||
const form = useForm<z.infer<typeof formSchema>>({
|
||||
@ -383,7 +392,7 @@ export default function CreateTriggerDialog({
|
||||
{t("triggers.dialog.form.actions.title")}
|
||||
</FormLabel>
|
||||
<div className="space-y-2">
|
||||
{["notification"].map((action) => (
|
||||
{availableActions.map((action) => (
|
||||
<div key={action} className="flex items-center space-x-2">
|
||||
<FormControl>
|
||||
<Checkbox
|
||||
|
||||
@ -243,6 +243,7 @@ export default function TriggerWizardDialog({
|
||||
<Step3ThresholdAndActions
|
||||
initialData={wizardState.step3Data}
|
||||
trigger={trigger}
|
||||
camera={selectedCamera}
|
||||
onNext={handleStep3Next}
|
||||
onBack={handleBack}
|
||||
isLoading={isLoading}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { useEffect, useCallback } from "react";
|
||||
import { useEffect, useCallback, useMemo } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
@ -17,6 +17,8 @@ import { Checkbox } from "@/components/ui/checkbox";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Trigger, TriggerAction } from "@/types/trigger";
|
||||
import ActivityIndicator from "@/components/indicators/activity-indicator";
|
||||
import useSWR from "swr";
|
||||
import { FrigateConfig } from "@/types/frigateConfig";
|
||||
|
||||
export type Step3FormData = {
|
||||
threshold: number;
|
||||
@ -26,6 +28,7 @@ export type Step3FormData = {
|
||||
type Step3ThresholdAndActionsProps = {
|
||||
initialData?: Step3FormData;
|
||||
trigger?: Trigger | null;
|
||||
camera: string;
|
||||
onNext: (data: Step3FormData) => void;
|
||||
onBack: () => void;
|
||||
isLoading?: boolean;
|
||||
@ -34,18 +37,29 @@ type Step3ThresholdAndActionsProps = {
|
||||
export default function Step3ThresholdAndActions({
|
||||
initialData,
|
||||
trigger,
|
||||
camera,
|
||||
onNext,
|
||||
onBack,
|
||||
isLoading = false,
|
||||
}: Step3ThresholdAndActionsProps) {
|
||||
const { t } = useTranslation("views/settings");
|
||||
const { data: config } = useSWR<FrigateConfig>("config");
|
||||
|
||||
const availableActions = useMemo(() => {
|
||||
if (!config) return [];
|
||||
|
||||
if (config.cameras[camera].notifications.enabled_in_config) {
|
||||
return ["notification", "sub_label", "attribute"];
|
||||
}
|
||||
return ["sub_label", "attribute"];
|
||||
}, [config, camera]);
|
||||
|
||||
const formSchema = z.object({
|
||||
threshold: z
|
||||
.number()
|
||||
.min(0, t("triggers.dialog.form.threshold.error.min"))
|
||||
.max(1, t("triggers.dialog.form.threshold.error.max")),
|
||||
actions: z.array(z.enum(["notification"])),
|
||||
actions: z.array(z.enum(["notification", "sub_label", "attribute"])),
|
||||
});
|
||||
|
||||
const form = useForm<z.infer<typeof formSchema>>({
|
||||
@ -127,7 +141,7 @@ export default function Step3ThresholdAndActions({
|
||||
<FormItem>
|
||||
<FormLabel>{t("triggers.dialog.form.actions.title")}</FormLabel>
|
||||
<div className="space-y-2">
|
||||
{["notification"].map((action) => (
|
||||
{availableActions.map((action) => (
|
||||
<div key={action} className="flex items-center space-x-2">
|
||||
<FormControl>
|
||||
<Checkbox
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
export type TriggerType = "thumbnail" | "description";
|
||||
export type TriggerAction = "notification";
|
||||
export type TriggerAction = "notification" | "sub_label" | "attribute";
|
||||
|
||||
export type Trigger = {
|
||||
enabled: boolean;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user