Skip to content

Rename nickname to friendly_name #19782

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 26, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/docs/configuration/reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -899,7 +899,7 @@ cameras:

# Optional: Configuration for triggers to automate actions based on semantic search results.
triggers:
# Required: Unique identifier for the trigger (generated automatically from nickname if not specified).
# Required: Unique identifier for the trigger (generated automatically from friendly_name if not specified).
trigger_name:
# Required: Enable or disable the trigger. (default: shown below)
enabled: true
Expand Down
4 changes: 2 additions & 2 deletions frigate/comms/webpush.py
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,7 @@ def send_alert(self, payload: dict[str, Any]) -> None:

camera: str = payload["after"]["camera"]
camera_name: str = getattr(
self.config.cameras[camera], "nickname", None
self.config.cameras[camera], "friendly_name", None
) or titlecase(camera.replace("_", " "))
current_time = datetime.datetime.now().timestamp()

Expand Down Expand Up @@ -410,7 +410,7 @@ def send_trigger(self, payload: dict[str, Any]) -> None:

camera: str = payload["camera"]
camera_name: str = getattr(
self.config.cameras[camera], "nickname", None
self.config.cameras[camera], "friendly_name", None
) or titlecase(camera.replace("_", " "))
current_time = datetime.datetime.now().timestamp()

Expand Down
8 changes: 5 additions & 3 deletions frigate/config/camera/camera.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,14 @@ class CameraTypeEnum(str, Enum):
class CameraConfig(FrigateBaseModel):
name: Optional[str] = Field(None, title="Camera name.", pattern=REGEX_CAMERA_NAME)

nickname: Optional[str] = Field(None, title="Camera nickname. Only for display.")
friendly_name: Optional[str] = Field(
None, title="Camera friendly name used in the Frigate UI."
)

@model_validator(mode="before")
@classmethod
def handle_nickname(cls, values):
if isinstance(values, dict) and "nickname" in values:
def handle_friendly_name(cls, values):
if isinstance(values, dict) and "friendly_name" in values:
pass
return values

Expand Down
2 changes: 1 addition & 1 deletion frigate/storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ def calculate_camera_usages(self) -> dict[str, dict]:
)

camera_key = (
getattr(self.config.cameras[camera], "nickname", None) or camera
getattr(self.config.cameras[camera], "friendly_name", None) or camera
)
usages[camera_key] = {
"usage": camera_storage,
Expand Down
4 changes: 2 additions & 2 deletions web/src/components/camera/CameraNameLabel.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as React from "react";
import * as LabelPrimitive from "@radix-ui/react-label";
import { useCameraNickname } from "@/hooks/use-camera-nickname";
import { useCameraFriendlyName } from "@/hooks/use-camera-friendly-name";
import { CameraConfig } from "@/types/frigateConfig";

interface CameraNameLabelProps
Expand All @@ -12,7 +12,7 @@ const CameraNameLabel = React.forwardRef<
React.ElementRef<typeof LabelPrimitive.Root>,
CameraNameLabelProps
>(({ className, camera, ...props }, ref) => {
const displayName = useCameraNickname(camera);
const displayName = useCameraFriendlyName(camera);
return (
<LabelPrimitive.Root ref={ref} className={className} {...props}>
{displayName}
Expand Down
4 changes: 2 additions & 2 deletions web/src/components/overlay/CameraInfoDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import axios from "axios";
import { toast } from "sonner";
import { Toaster } from "../ui/sonner";
import { Trans, useTranslation } from "react-i18next";
import { useCameraNickname } from "@/hooks/use-camera-nickname";
import { useCameraFriendlyName } from "@/hooks/use-camera-friendly-name";

type CameraInfoDialogProps = {
camera: CameraConfig;
Expand Down Expand Up @@ -75,7 +75,7 @@ export default function CameraInfoDialog({
return b === 0 ? a : gcd(b, a % b);
}

const cameraName = useCameraNickname(camera);
const cameraName = useCameraFriendlyName(camera);

return (
<>
Expand Down
4 changes: 2 additions & 2 deletions web/src/components/overlay/CreateTriggerDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ import ImagePicker from "@/components/overlay/ImagePicker";
import { Trigger, TriggerAction, TriggerType } from "@/types/trigger";
import { Switch } from "@/components/ui/switch";
import { Textarea } from "../ui/textarea";
import { useCameraNickname } from "@/hooks/use-camera-nickname";
import { useCameraFriendlyName } from "@/hooks/use-camera-friendly-name";

type CreateTriggerDialogProps = {
show: boolean;
Expand Down Expand Up @@ -162,7 +162,7 @@ export default function CreateTriggerDialog({
onCancel();
};

const cameraName = useCameraNickname(selectedCamera);
const cameraName = useCameraFriendlyName(selectedCamera);

return (
<Dialog open={show} onOpenChange={onCancel}>
Expand Down
4 changes: 2 additions & 2 deletions web/src/components/player/LivePlayer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import { baseUrl } from "@/api/baseUrl";
import { PlayerStats } from "./PlayerStats";
import { LuVideoOff } from "react-icons/lu";
import { Trans, useTranslation } from "react-i18next";
import { useCameraNickname } from "@/hooks/use-camera-nickname";
import { useCameraFriendlyName } from "@/hooks/use-camera-friendly-name";

type LivePlayerProps = {
cameraRef?: (ref: HTMLDivElement | null) => void;
Expand Down Expand Up @@ -77,7 +77,7 @@ export default function LivePlayer({

const internalContainerRef = useRef<HTMLDivElement | null>(null);

const cameraName = useCameraNickname(cameraConfig);
const cameraName = useCameraFriendlyName(cameraConfig);
// stats

const [stats, setStats] = useState<PlayerStatsType>({
Expand Down
6 changes: 3 additions & 3 deletions web/src/components/player/PreviewPlayer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import {
usePreviewForTimeRange,
} from "@/hooks/use-camera-previews";
import { useTranslation } from "react-i18next";
import { useCameraNickname } from "@/hooks/use-camera-nickname";
import { useCameraFriendlyName } from "@/hooks/use-camera-friendly-name";

type PreviewPlayerProps = {
previewRef?: (ref: HTMLDivElement | null) => void;
Expand Down Expand Up @@ -149,7 +149,7 @@ function PreviewVideoPlayer({
const { t } = useTranslation(["components/player"]);
const { data: config } = useSWR<FrigateConfig>("config");

const cameraName = useCameraNickname(camera);
const cameraName = useCameraFriendlyName(camera);
// controlling playback

const previewRef = useRef<HTMLVideoElement | null>(null);
Expand Down Expand Up @@ -466,7 +466,7 @@ function PreviewFramesPlayer({
}: PreviewFramesPlayerProps) {
const { t } = useTranslation(["components/player"]);

const cameraName = useCameraNickname(camera);
const cameraName = useCameraFriendlyName(camera);
// frames data

const { data: previewFrames } = useSWR<string[]>(
Expand Down
14 changes: 7 additions & 7 deletions web/src/components/settings/CameraEditForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ export default function CameraEditForm({
const cameraInfo = useMemo(() => {
if (!cameraName || !config?.cameras[cameraName]) {
return {
nickname: undefined,
friendly_name: undefined,
name: cameraName || "",
roles: new Set<Role>(),
};
Expand All @@ -121,14 +121,14 @@ export default function CameraEditForm({
});

return {
nickname: camera?.nickname || cameraName,
friendly_name: camera?.friendly_name || cameraName,
name: cameraName,
roles,
};
}, [cameraName, config]);

const defaultValues: FormValues = {
cameraName: cameraInfo?.nickname || cameraName || "",
cameraName: cameraInfo?.friendly_name || cameraName || "",
enabled: true,
ffmpeg: {
inputs: [
Expand Down Expand Up @@ -169,18 +169,18 @@ export default function CameraEditForm({
const saveCameraConfig = (values: FormValues) => {
setIsLoading(true);
let finalCameraName = values.cameraName;
let nickname: string | undefined = undefined;
let friendly_name: string | undefined = undefined;
const isValidName = /^[a-zA-Z0-9_-]+$/.test(values.cameraName);
if (!isValidName) {
finalCameraName = generateFixedHash(finalCameraName);
nickname = values.cameraName;
friendly_name = values.cameraName;
}

const configData: ConfigSetBody["config_data"] = {
cameras: {
[finalCameraName]: {
enabled: values.enabled,
...(nickname && { nickname }),
...(friendly_name && { friendly_name }),
ffmpeg: {
inputs: values.ffmpeg.inputs.map((input) => ({
path: input.path,
Expand Down Expand Up @@ -235,7 +235,7 @@ export default function CameraEditForm({
if (
cameraName &&
values.cameraName !== cameraName &&
values.cameraName !== cameraInfo?.nickname
values.cameraName !== cameraInfo?.friendly_name
) {
// If camera name changed, delete old camera config
const deleteRequestBody: ConfigSetBody = {
Expand Down
4 changes: 2 additions & 2 deletions web/src/components/settings/CameraStreamingDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ import { Link } from "react-router-dom";
import { LiveStreamMetadata } from "@/types/live";
import { Trans, useTranslation } from "react-i18next";
import { useDocDomain } from "@/hooks/use-doc-domain";
import { useCameraNickname } from "@/hooks/use-camera-nickname";
import { useCameraFriendlyName } from "@/hooks/use-camera-friendly-name";

type CameraStreamingDialogProps = {
camera: string;
Expand All @@ -57,7 +57,7 @@ export function CameraStreamingDialog({
const { getLocaleDocUrl } = useDocDomain();
const { data: config } = useSWR<FrigateConfig>("config");

const cameraName = useCameraNickname(camera);
const cameraName = useCameraFriendlyName(camera);

const [isLoading, setIsLoading] = useState(false);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ export function resolveCameraName(
) {
if (typeof cameraId === "object" && cameraId !== null) {
const camera = cameraId as CameraConfig;
return camera?.nickname || camera?.name.replaceAll("_", " ");
return camera?.friendly_name || camera?.name.replaceAll("_", " ");
} else {
const camera = config?.cameras?.[String(cameraId)];
return camera?.nickname || String(cameraId).replaceAll("_", " ");
return camera?.friendly_name || String(cameraId).replaceAll("_", " ");
}
}

export function useCameraNickname(
export function useCameraFriendlyName(
cameraId: string | CameraConfig | undefined,
): string {
const { data: config } = useSWR<FrigateConfig>("config");
Expand Down
4 changes: 2 additions & 2 deletions web/src/hooks/use-stats.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ export default function useStats(stats: FrigateStats | undefined) {
return;
}

const cameraName = config.cameras?.[name]?.nickname ?? name;
const cameraName = config.cameras?.[name]?.friendly_name ?? name;
if (config.cameras[name].enabled && cam["camera_fps"] == 0) {
problems.push({
text: t("stats.cameraIsOffline", {
Expand All @@ -82,7 +82,7 @@ export default function useStats(stats: FrigateStats | undefined) {
memoizedStats["cpu_usages"][cam["pid"]]?.cpu_average,
);

const cameraName = config?.cameras?.[name]?.nickname ?? name;
const cameraName = config?.cameras?.[name]?.friendly_name ?? name;
if (!isNaN(ffmpegAvg) && ffmpegAvg >= CameraFfmpegThreshold.error) {
problems.push({
text: t("stats.ffmpegHighCpuUsage", {
Expand Down
2 changes: 1 addition & 1 deletion web/src/types/frigateConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export type SearchModel = "jinav1" | "jinav2";
export type SearchModelSize = "small" | "large";

export interface CameraConfig {
nickname: string;
friendly_name: string;
audio: {
enabled: boolean;
enabled_in_config: boolean;
Expand Down
4 changes: 2 additions & 2 deletions web/src/views/settings/CameraSettingsView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ import {
} from "@/components/ui/select";
import { IoMdArrowRoundBack } from "react-icons/io";
import { isDesktop } from "react-device-detect";
import { useCameraNickname } from "@/hooks/use-camera-nickname";
import { useCameraFriendlyName } from "@/hooks/use-camera-friendly-name";
import { CameraNameLabel } from "@/components/camera/CameraNameLabel";

type CameraSettingsViewProps = {
Expand Down Expand Up @@ -98,7 +98,7 @@ export default function CameraSettingsView({
return [];
}, [config]);

const selectCameraName = useCameraNickname(selectedCamera);
const selectCameraName = useCameraFriendlyName(selectedCamera);

// zones and labels

Expand Down
4 changes: 2 additions & 2 deletions web/src/views/settings/ObjectSettingsView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import { isDesktop } from "react-device-detect";
import { Trans, useTranslation } from "react-i18next";
import { useDocDomain } from "@/hooks/use-doc-domain";
import { getTranslatedLabel } from "@/utils/i18n";
import { useCameraNickname } from "@/hooks/use-camera-nickname";
import { useCameraFriendlyName } from "@/hooks/use-camera-friendly-name";
import { AudioLevelGraph } from "@/components/audio/AudioLevelGraph";
import { useWs } from "@/api/ws";

Expand Down Expand Up @@ -129,7 +129,7 @@ export default function ObjectSettingsView({
}
}, [config, selectedCamera]);

const cameraName = useCameraNickname(cameraConfig);
const cameraName = useCameraFriendlyName(cameraConfig);

const { objects, audio_detections } = useCameraActivity(
cameraConfig ?? ({} as CameraConfig),
Expand Down
4 changes: 2 additions & 2 deletions web/src/views/settings/TriggerView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import { cn } from "@/lib/utils";
import { formatUnixTimestampToDateTime } from "@/utils/dateUtil";
import { Link } from "react-router-dom";
import { useTriggers } from "@/api/ws";
import { useCameraNickname } from "@/hooks/use-camera-nickname";
import { useCameraFriendlyName } from "@/hooks/use-camera-friendly-name";

type ConfigSetBody = {
requires_restart: number;
Expand Down Expand Up @@ -79,7 +79,7 @@ export default function TriggerView({
const [triggeredTrigger, setTriggeredTrigger] = useState<string>();
const [isLoading, setIsLoading] = useState(false);

const cameraName = useCameraNickname(selectedCamera);
const cameraName = useCameraFriendlyName(selectedCamera);
const triggers = useMemo(() => {
if (
!config ||
Expand Down
2 changes: 1 addition & 1 deletion web/src/views/system/CameraMetrics.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
import useSWR from "swr";
import { useTranslation } from "react-i18next";
import { CameraNameLabel } from "@/components/camera/CameraNameLabel";
import { resolveCameraName } from "@/hooks/use-camera-nickname";
import { resolveCameraName } from "@/hooks/use-camera-friendly-name";

type CameraMetricsProps = {
lastUpdated: number;
Expand Down