diff --git a/src/frigate-card.ts b/src/frigate-card.ts index 165dbb64..5d064c5a 100644 --- a/src/frigate-card.ts +++ b/src/frigate-card.ts @@ -30,7 +30,7 @@ import './editor'; import style from './frigate-card.scss' -import { frigateGetEventsResponseSchema } from './types'; +import { frigateCardConfigSchema, frigateGetEventsResponseSchema } from './types'; import type { FrigateCardConfig, FrigateEvent, FrigateGetEventsResponse, GetEventsParameters, ControlVideosParameters } from './types'; import { actionHandler } from './action-handler-directive'; import { CARD_VERSION } from './const'; @@ -100,42 +100,29 @@ export class FrigateCard extends LitElement { if (!inputConfig) { throw new Error(localize('common.invalid_configuration:')); } - // inputConfig is not "extensible" (i.e. preventExtensions() has been - // called on it), need to make a copy to allow modifications. - const cardConfig = Object.assign({ - name: 'Frigate' - }, inputConfig); - if (cardConfig.test_gui) { + const parseResult = frigateCardConfigSchema.safeParse(inputConfig); + if (!parseResult.success) { + const errors = parseResult.error.format() + const keys = Object.keys(errors).filter(v => !v.startsWith("_")); + throw new Error(localize('common.invalid_configuration') + ": " + keys.join(", ")); + } + const config = parseResult.data; + + if (config.test_gui) { getLovelace().setEditMode(true); } - if (!cardConfig.frigate_url) { - throw new Error(localize('common.invalid_configuration_missing') + ": frigate_url"); - } - - if (!cardConfig.frigate_camera_name) { + if (!config.frigate_camera_name) { // No camera name specified, so just assume it's the same as the entity name. - if (cardConfig.camera_entity.includes(".")) { - cardConfig.frigate_camera_name = cardConfig.camera_entity.split('.', 2)[1] + if (config.camera_entity.includes(".")) { + config.frigate_camera_name = config.camera_entity.split('.', 2)[1] } else { - throw new Error(localize('common.invalid_configuration_missing') + ": camera_entity"); + throw new Error(localize('common.invalid_configuration') + ": camera_entity"); } } - if (cardConfig.view_timeout) { - if (isNaN(Number(cardConfig.view_timeout))) { - throw new Error(localize('common.invalid_configuration') + ": view_timeout"); - } - } - - if (cardConfig.view_default) { - if (!["live", "clips", "clip", "snapshots", "snapshot"].includes(cardConfig.view_default)) { - throw new Error(localize('common.invalid_configuration') + ": view_default"); - } - } - - this.config = cardConfig; + this.config = config; this._setViewModeToDefault(); } diff --git a/src/localize/languages/en.json b/src/localize/languages/en.json index 7cf83f7c..e5a4b4d5 100644 --- a/src/localize/languages/en.json +++ b/src/localize/languages/en.json @@ -2,7 +2,6 @@ "common": { "version": "Version", "invalid_configuration": "Invalid configuration", - "invalid_configuration_missing": "Missing parameter in configuration", "show_warning": "Show Warning", "show_error": "Show Error" } diff --git a/src/types.ts b/src/types.ts index 40e2ab74..6bacc9f7 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,5 +1,6 @@ import { ActionConfig, LovelaceCard, LovelaceCardConfig, LovelaceCardEditor } from 'custom-card-helpers'; import { z } from "zod"; + declare global { interface HTMLElementTagNameMap { 'frigate-card-editor': LovelaceCardEditor; @@ -10,24 +11,22 @@ declare global { /** * Internal types. */ -export interface FrigateCardConfig extends LovelaceCardConfig { - type: string; - name?: string; - camera_entity: string; - motion_entity: string | null; - frigate_url: string; - frigate_camera_name?: string | null; - default_view: string | null; - timeout_ms?: number | null; +export const frigateCardConfigSchema = z.object({ + camera_entity: z.string(), + motion_entity: z.string().optional(), + frigate_url: z.string().url(), + frigate_camera_name: z.string().optional(), + view_default: z.enum(["live", "clips", "clip", "snapshots", "snapshot"]).optional().default("live"), + view_timeout: z.number().optional(), + label: z.string().optional(), + + show_warning: z.boolean().optional(), + show_error: z.boolean().optional(), + test_gui: z.boolean().optional(), +}) +export type FrigateCardConfig = z.infer; - show_warning?: boolean; - show_error?: boolean; - test_gui?: boolean; - tap_action?: ActionConfig; - hold_action?: ActionConfig; - double_tap_action?: ActionConfig; -} export interface GetEventsParameters { has_clip?: boolean;