Convert camera configuration to YAML dictionary.

This commit is contained in:
Dermot Duffy
2022-01-14 21:31:16 -08:00
parent af3b9092fd
commit 85ba8f3a93
5 changed files with 68 additions and 57 deletions
+30 -32
View File
@@ -1,5 +1,3 @@
// TODO change url to frigate_url?
/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-explicit-any */
import { import {
CSSResultGroup, CSSResultGroup,
@@ -33,7 +31,6 @@ import {
CameraConfig, CameraConfig,
} from './types.js'; } from './types.js';
import type { import type {
BrowseMediaQueryParameters,
Entity, Entity,
ExtendedHomeAssistant, ExtendedHomeAssistant,
FrigateCardConfig, FrigateCardConfig,
@@ -53,6 +50,7 @@ import {
homeAssistantSignPath, homeAssistantSignPath,
homeAssistantWSRequest, homeAssistantWSRequest,
isValidMediaShowInfo, isValidMediaShowInfo,
refreshCameraConfigDynamicParameters,
shouldUpdateBasedOnHass, shouldUpdateBasedOnHass,
} from './common.js'; } from './common.js';
import { localize } from './localize/localize.js'; import { localize } from './localize/localize.js';
@@ -280,18 +278,24 @@ export class FrigateCard extends LitElement {
} }
if (this.config.menu.buttons.cameras && this._cameras && this._cameras.size > 1) { if (this.config.menu.buttons.cameras && this._cameras && this._cameras.size > 1) {
const menuItems = Array.from(this._cameras, ([camera, config]) => ({ const menuItems = Array.from(this._cameras, ([camera, config]) => {
icon: config.icon || 'mdi:cctv', const dynamicConfig = refreshCameraConfigDynamicParameters(
entity: config.camera_entity, { ...config },
this._hass,
);
return {
icon: dynamicConfig.icon || 'mdi:cctv',
entity: dynamicConfig.camera_entity,
state_color: true, state_color: true,
title: config.title, title: dynamicConfig.title,
tap_action: createFrigateCardCustomAction('camera_select', camera), tap_action: createFrigateCardCustomAction('camera_select', camera),
})); };
});
buttons.push({ buttons.push({
type: 'custom:frigate-card-menu-submenu', type: 'custom:frigate-card-menu-submenu',
title: localize('config.menu.buttons.cameras'), title: localize('config.menu.buttons.cameras'),
icon: 'mdi:camera-switch', icon: 'mdi:video-switch',
items: menuItems, items: menuItems,
}); });
} }
@@ -399,8 +403,13 @@ export class FrigateCard extends LitElement {
protected async _loadCameras(): Promise<void> { protected async _loadCameras(): Promise<void> {
const cameras: Map<string, CameraConfig> = new Map(); const cameras: Map<string, CameraConfig> = new Map();
const addCameraConfig = async (config: CameraConfig) => { const addCameraConfig = async (id: string, config: CameraConfig) => {
if (!config.camera_name && config.camera_entity) { if (!config.camera_name && (config.camera_entity || id.startsWith('camera.'))) {
if (!config.camera_entity) {
// If the camera_entity isn't explicitly set and the id looks like
// one, use that.
config.camera_entity = id;
}
const resolvedName = await this._getFrigateCameraNameFromEntity( const resolvedName = await this._getFrigateCameraNameFromEntity(
config.camera_entity, config.camera_entity,
); );
@@ -408,30 +417,17 @@ export class FrigateCard extends LitElement {
config.camera_name = resolvedName; config.camera_name = resolvedName;
} }
} }
if (!config.camera_name) {
if (config.camera_name) { config.camera_name = id;
const id = config.id || config.camera_name;
if (cameras.has(id)) {
this._setMessageAndUpdate(
{
message: localize('error.duplicate_frigate_camera_name'),
type: 'error',
},
true,
);
} else {
cameras.set(config.id || config.camera_name, config);
}
} }
cameras.set(id, config);
}; };
if (this.config.camera) { await Promise.all(
if (Array.isArray(this.config.camera)) { Object.keys(this.config.cameras).map((key) =>
await Promise.all(this.config.camera.map(addCameraConfig.bind(this))); addCameraConfig(key, this.config.cameras[key]),
} else { ),
await addCameraConfig(this.config.camera); );
}
}
if (!cameras.size) { if (!cameras.size) {
return this._setMessageAndUpdate( return this._setMessageAndUpdate(
@@ -945,6 +941,8 @@ export class FrigateCard extends LitElement {
if (requestRefresh) { if (requestRefresh) {
this.requestUpdate(); this.requestUpdate();
} }
// TODO: Remove
console.info(`Showing media: ${JSON.stringify(mediaShowInfo)}`);
} }
/** /**
+22 -10
View File
@@ -287,7 +287,8 @@ export function convertActionToFrigateCardCustomAction(
*/ */
export function createFrigateCardCustomAction( export function createFrigateCardCustomAction(
action: FrigateCardAction, action: FrigateCardAction,
camera?: string): FrigateCardCustomAction | undefined { camera?: string,
): FrigateCardCustomAction | undefined {
if (action == 'camera_select') { if (action == 'camera_select') {
if (!camera) { if (!camera) {
return undefined; return undefined;
@@ -296,12 +297,12 @@ export function createFrigateCardCustomAction(
action: 'fire-dom-event', action: 'fire-dom-event',
frigate_card_action: action, frigate_card_action: action,
camera: camera, camera: camera,
} };
} }
return { return {
action: 'fire-dom-event', action: 'fire-dom-event',
frigate_card_action: action, frigate_card_action: action,
} };
} }
/** /**
@@ -397,13 +398,24 @@ export function refreshDynamicStateParameters(
return params; return params;
} }
function prettifyCameraName(input: string): string {
const words = input.split(/[_\s]+/);
return words
.map((word) => {
return word[0].toUpperCase() + word.substring(1);
})
.join(' ');
}
export function refreshCameraConfigDynamicParameters( export function refreshCameraConfigDynamicParameters(
hass: HomeAssistant, config: CameraConfig,
config: CameraConfig): CameraConfig { hass?: HomeAssistant,
if (hass && config.camera_entity) { ): CameraConfig {
const state = hass.states[config.camera_entity]; const state =
config.title = config.title ?? (state?.attributes?.friendly_name || config.camera_entity); hass && config.camera_entity ? hass.states[config.camera_entity] : undefined;
config.icon = config.icon ?? stateIcon(state); config.title =
} config.title ??
(state?.attributes?.friendly_name || prettifyCameraName(config.camera_name || ''));
config.icon = config.icon ?? (state ? stateIcon(state) : 'mdi:video');
return config; return config;
} }
+7 -5
View File
@@ -1,4 +1,8 @@
// TODO editor // TODO editor
// TODO webrtc entities in camera section?
// TODO conditional elements based on camera name (requires event changed to propagate upwards)
// TODO change url to frigate_url?
// TODO Remove media load event warning
// TODO readme // TODO readme
// TODO search for TODOs // TODO search for TODOs
@@ -237,12 +241,10 @@ export class FrigateCardLiveCarousel extends FrigateCardMediaCarousel {
} }
return Array.from(this.cameras.values()).map((cameraConfig, index) => { return Array.from(this.cameras.values()).map((cameraConfig, index) => {
let refreshedConfig = { ...cameraConfig }; let refreshedConfig = { ...cameraConfig };
if (this.hass) {
refreshedConfig = refreshCameraConfigDynamicParameters( refreshedConfig = refreshCameraConfigDynamicParameters(
this.hass,
refreshedConfig, refreshedConfig,
this.hass,
); );
}
return this._renderLive(refreshedConfig, index); return this._renderLive(refreshedConfig, index);
}); });
} }
@@ -303,13 +305,13 @@ export class FrigateCardLiveCarousel extends FrigateCardMediaCarousel {
if (currentIndex > 0) { if (currentIndex > 0) {
prev = this.cameras.get(keys[currentIndex - 1]) ?? null; prev = this.cameras.get(keys[currentIndex - 1]) ?? null;
if (prev) { if (prev) {
prev = refreshCameraConfigDynamicParameters(this.hass, { ...prev }); prev = refreshCameraConfigDynamicParameters({ ...prev }, this.hass);
} }
} }
if (currentIndex + 1 < this.cameras.size) { if (currentIndex + 1 < this.cameras.size) {
next = this.cameras.get(keys[currentIndex + 1]) ?? null; next = this.cameras.get(keys[currentIndex + 1]) ?? null;
if (next) { if (next) {
next = refreshCameraConfigDynamicParameters(this.hass, { ...next }); next = refreshCameraConfigDynamicParameters({ ...next }, this.hass);
} }
} }
return [prev, next]; return [prev, next];
-1
View File
@@ -154,7 +154,6 @@
"upgrade_available": "An automated card configuration upgrade is available, please visit the visual card editor", "upgrade_available": "An automated card configuration upgrade is available, please visit the visual card editor",
"missing_webrtc": "WebRTC component not found", "missing_webrtc": "WebRTC component not found",
"no_cameras": "No cameras found, you must configure at least one camera configured with a camera_entity or camera_name", "no_cameras": "No cameras found, you must configure at least one camera configured with a camera_entity or camera_name",
"duplicate_frigate_camera_name": "Duplicate Frigate camera name, use the 'id' parameter to uniquely identify them",
"could_not_render_elements": "Could not render picture elements", "could_not_render_elements": "Could not render picture elements",
"invalid_elements_config": "Invalid picture elements configuration", "invalid_elements_config": "Invalid picture elements configuration",
"jsmpeg_no_sign": "Could not retrieve or sign JSMPEG websocket path", "jsmpeg_no_sign": "Could not retrieve or sign JSMPEG websocket path",
+3 -3
View File
@@ -368,12 +368,12 @@ const cameraConfigDefaultSchema = z
// specified). // specified).
icon: z.string().optional(), icon: z.string().optional(),
title: z.string().optional(), title: z.string().optional(),
id: z.string().optional(),
}) })
.default(cameraConfigDefault); .default(cameraConfigDefault);
export type CameraConfig = z.infer<typeof cameraConfigDefaultSchema>; export type CameraConfig = z.infer<typeof cameraConfigDefaultSchema>;
const camerasConfigDefaultSchema = z.record(cameraConfigDefaultSchema);
/** /**
* View configuration section. * View configuration section.
*/ */
@@ -650,7 +650,7 @@ const dimensionsConfigSchema = z
*/ */
export const frigateCardConfigSchema = z.object({ export const frigateCardConfigSchema = z.object({
// Main configuration sections. // Main configuration sections.
camera: cameraConfigDefaultSchema.or(cameraConfigDefaultSchema.array().nonempty()), cameras: camerasConfigDefaultSchema,
view: viewConfigSchema, view: viewConfigSchema,
menu: menuConfigSchema, menu: menuConfigSchema,
live: liveConfigSchema, live: liveConfigSchema,