show frigate+ model info in detection model settings when using a frigate+ model

This commit is contained in:
Josh Hawkins 2026-02-13 10:17:34 -06:00
parent fdf7e22130
commit b60e66f224
6 changed files with 165 additions and 68 deletions

View File

@ -984,8 +984,8 @@
"title": "Frigate+ Settings",
"cardTitles": {
"api": "API",
"currentModel": "Current model",
"otherModels": "Other models",
"currentModel": "Current Model",
"otherModels": "Other Models",
"configuration": "Configuration"
},
"apiKey": {
@ -1029,6 +1029,15 @@
"error": "Failed to save config changes: {{errorMessage}}"
}
},
"detectionModel": {
"plusActive": {
"title": "Frigate+ model management",
"label": "Current model source",
"description": "This instance is running a Frigate+ model. Select or change your model in Frigate+ settings.",
"goToFrigatePlus": "Go to Frigate+ settings",
"showModelForm": "Manually configure a model"
}
},
"triggers": {
"documentTitle": "Triggers",
"semanticSearch": {

View File

@ -41,6 +41,7 @@ import RolesView from "@/views/settings/RolesView";
import UiSettingsView from "@/views/settings/UiSettingsView";
import FrigatePlusSettingsView from "@/views/settings/FrigatePlusSettingsView";
import MaintenanceSettingsView from "@/views/settings/MaintenanceSettingsView";
import SystemDetectionModelSettingsView from "@/views/settings/SystemDetectionModelSettingsView";
import {
SingleSectionPage,
type SettingsPageProps,
@ -239,7 +240,7 @@ const SystemDetectorHardwareSettingsPage = createSectionPage(
"detectors",
"global",
);
const SystemDetectionModelSettingsPage = createSectionPage("model", "global");
const SystemDetectionModelSettingsPage = SystemDetectionModelSettingsView;
const NotificationsSettingsPage = createSectionPage("notifications", "global");
const SystemMqttSettingsPage = createSectionPage("mqtt", "global");

View File

@ -467,7 +467,7 @@ export interface FrigateConfig {
supportedDetectors: string[];
width: number;
height: number;
};
} | null;
};
motion: Record<string, unknown> | null;

View File

@ -26,6 +26,7 @@ import {
SettingsGroupCard,
SplitCardRow,
} from "@/components/card/SettingsGroupCard";
import FrigatePlusCurrentModelSummary from "@/views/settings/components/FrigatePlusCurrentModelSummary";
type FrigatePlusModel = {
id: string;
@ -259,70 +260,7 @@ export default function FrigatePlusSettingsView({
</SettingsGroupCard>
{config?.model.plus && (
<SettingsGroupCard
title={t("frigatePlus.cardTitles.currentModel")}
>
{!config?.model?.plus && (
<p className="text-muted-foreground">
{t("frigatePlus.modelInfo.loading")}
</p>
)}
{config?.model?.plus === null && (
<p className="text-danger">
{t("frigatePlus.modelInfo.error")}
</p>
)}
{config?.model?.plus && (
<div className="space-y-6">
<SplitCardRow
label={t("frigatePlus.modelInfo.baseModel")}
content={
<p>
{config.model.plus.baseModel} (
{config.model.plus.isBaseModel
? t(
"frigatePlus.modelInfo.plusModelType.baseModel",
)
: t(
"frigatePlus.modelInfo.plusModelType.userModel",
)}
)
</p>
}
/>
<SplitCardRow
label={t("frigatePlus.modelInfo.trainDate")}
content={
<p>
{new Date(
config.model.plus.trainDate,
).toLocaleString()}
</p>
}
/>
<SplitCardRow
label={t("frigatePlus.modelInfo.modelType")}
content={
<p>
{config.model.plus.name} (
{config.model.plus.width +
"x" +
config.model.plus.height}
)
</p>
}
/>
<SplitCardRow
label={t("frigatePlus.modelInfo.supportedDetectors")}
content={
<p>
{config.model.plus.supportedDetectors.join(", ")}
</p>
}
/>
</div>
)}
</SettingsGroupCard>
<FrigatePlusCurrentModelSummary plusModel={config.model.plus} />
)}
{config?.model.plus && (

View File

@ -0,0 +1,88 @@
import ActivityIndicator from "@/components/indicators/activity-indicator";
import Heading from "@/components/ui/heading";
import { Button } from "@/components/ui/button";
import { useState } from "react";
import { useNavigate } from "react-router-dom";
import useSWR from "swr";
import type { FrigateConfig } from "@/types/frigateConfig";
import {
SettingsGroupCard,
SplitCardRow,
} from "@/components/card/SettingsGroupCard";
import {
SingleSectionPage,
type SettingsPageProps,
} from "@/views/settings/SingleSectionPage";
import FrigatePlusCurrentModelSummary from "@/views/settings/components/FrigatePlusCurrentModelSummary";
import { useTranslation } from "react-i18next";
export default function SystemDetectionModelSettingsView(
props: SettingsPageProps,
) {
const { t } = useTranslation(["config/global", "views/settings"]);
const { data: config } = useSWR<FrigateConfig>("config");
const [showModelForm, setShowModelForm] = useState(false);
const navigate = useNavigate();
if (!config) {
return <ActivityIndicator />;
}
const isPlusModelActive = Boolean(config?.model?.plus?.id);
if (!isPlusModelActive || showModelForm) {
return <SingleSectionPage sectionKey="model" level="global" {...props} />;
}
return (
<div className="flex size-full max-w-5xl flex-col lg:pr-2">
<div className="mb-5 flex items-center justify-between gap-4">
<div className="flex flex-col">
<Heading as="h4">{t("model.label", { ns: "config/global" })}</Heading>
<div className="my-1 text-sm text-muted-foreground">
{t("model.description", { ns: "config/global" })}
</div>
</div>
</div>
<div className="space-y-6">
<SettingsGroupCard
title={t("detectionModel.plusActive.title", { ns: "views/settings" })}
>
<SplitCardRow
label={t("detectionModel.plusActive.label", {
ns: "views/settings",
})}
description={t("detectionModel.plusActive.description", {
ns: "views/settings",
})}
content={
<div className="flex flex-col items-start gap-3">
<Button
variant="outline"
onClick={() => navigate("/settings?page=frigateplus")}
>
{t("detectionModel.plusActive.goToFrigatePlus", {
ns: "views/settings",
})}
</Button>
<Button
type="button"
variant="outline"
size="sm"
onClick={() => setShowModelForm(true)}
>
{t("detectionModel.plusActive.showModelForm", {
ns: "views/settings",
})}
</Button>
</div>
}
/>
</SettingsGroupCard>
<FrigatePlusCurrentModelSummary plusModel={config.model.plus} />
</div>
</div>
);
}

View File

@ -0,0 +1,61 @@
import {
SettingsGroupCard,
SplitCardRow,
} from "@/components/card/SettingsGroupCard";
import type { FrigateConfig } from "@/types/frigateConfig";
import { useTranslation } from "react-i18next";
type FrigatePlusCurrentModelSummaryProps = {
plusModel: FrigateConfig["model"]["plus"];
};
export default function FrigatePlusCurrentModelSummary({
plusModel,
}: FrigatePlusCurrentModelSummaryProps) {
const { t } = useTranslation("views/settings");
return (
<SettingsGroupCard title={t("frigatePlus.cardTitles.currentModel")}>
{plusModel === undefined && (
<p className="text-muted-foreground">
{t("frigatePlus.modelInfo.loading")}
</p>
)}
{plusModel === null && (
<p className="text-danger">{t("frigatePlus.modelInfo.error")}</p>
)}
{plusModel && (
<div className="space-y-6">
<SplitCardRow
label={t("frigatePlus.modelInfo.baseModel")}
content={
<p>
{plusModel.baseModel} (
{plusModel.isBaseModel
? t("frigatePlus.modelInfo.plusModelType.baseModel")
: t("frigatePlus.modelInfo.plusModelType.userModel")}
)
</p>
}
/>
<SplitCardRow
label={t("frigatePlus.modelInfo.trainDate")}
content={<p>{new Date(plusModel.trainDate).toLocaleString()}</p>}
/>
<SplitCardRow
label={t("frigatePlus.modelInfo.modelType")}
content={
<p>
{plusModel.name} ({plusModel.width + "x" + plusModel.height})
</p>
}
/>
<SplitCardRow
label={t("frigatePlus.modelInfo.supportedDetectors")}
content={<p>{plusModel.supportedDetectors.join(", ")}</p>}
/>
</div>
)}
</SettingsGroupCard>
);
}