Move camera name and icon into camera manager.

This commit is contained in:
Dermot Duffy
2023-01-29 18:30:07 -08:00
parent eed436d77f
commit 0df6faaf7d
15 changed files with 165 additions and 110 deletions
+6
View File
@@ -16,6 +16,7 @@ import {
RecordingSegmentsQueryResultsMap, RecordingSegmentsQueryResultsMap,
CameraManagerEngineCapabilities, CameraManagerEngineCapabilities,
CameraManagerMediaCapabilities, CameraManagerMediaCapabilities,
CameraManagerCameraMetadata,
} from './types'; } from './types';
export const CAMERA_MANAGER_ENGINE_EVENT_LIMIT_DEFAULT = 10000; export const CAMERA_MANAGER_ENGINE_EVENT_LIMIT_DEFAULT = 10000;
@@ -94,6 +95,11 @@ export interface CameraManagerEngine {
cameras: Map<string, CameraConfig>, cameras: Map<string, CameraConfig>,
): Promise<MediaMetadata | null>; ): Promise<MediaMetadata | null>;
getCameraMetadata(
hass: HomeAssistant,
cameraConfig: CameraConfig,
): CameraManagerCameraMetadata;
getCapabilities(): CameraManagerEngineCapabilities | null; getCapabilities(): CameraManagerEngineCapabilities | null;
getMediaCapabilities(media: ViewMedia): CameraManagerMediaCapabilities | null; getMediaCapabilities(media: ViewMedia): CameraManagerMediaCapabilities | null;
} }
+28 -2
View File
@@ -12,6 +12,7 @@ import {
} from '../engine'; } from '../engine';
import { DateRange } from '../range'; import { DateRange } from '../range';
import { import {
CameraManagerCameraMetadata,
CameraManagerEngineCapabilities, CameraManagerEngineCapabilities,
CameraManagerMediaCapabilities, CameraManagerMediaCapabilities,
DataQuery, DataQuery,
@@ -48,13 +49,19 @@ import {
} from './requests'; } from './requests';
import orderBy from 'lodash-es/orderBy'; import orderBy from 'lodash-es/orderBy';
import throttle from 'lodash-es/throttle'; import throttle from 'lodash-es/throttle';
import { allPromises, formatDate, runWhenIdleIfSupported } from '../../utils/basic'; import {
allPromises,
formatDate,
prettifyTitle,
runWhenIdleIfSupported,
} from '../../utils/basic';
import { fromUnixTime } from 'date-fns'; import { fromUnixTime } from 'date-fns';
import { sum } from 'lodash-es'; import { sum } from 'lodash-es';
import { FrigateViewMediaClassifier } from './media-classifier'; import { FrigateViewMediaClassifier } from './media-classifier';
import { ViewMediaClassifier } from '../../view/media-classifier'; import { ViewMediaClassifier } from '../../view/media-classifier';
import { FrigateViewMediaFactory } from './media'; import { FrigateViewMediaFactory } from './media';
import { log } from '../../utils/debug'; import { log } from '../../utils/debug';
import { getEntityIcon, getEntityTitle } from '../../utils/ha';
const EVENT_REQUEST_CACHE_MAX_AGE_SECONDS = 60; const EVENT_REQUEST_CACHE_MAX_AGE_SECONDS = 60;
const RECORDING_SUMMARY_REQUEST_CACHE_MAX_AGE_SECONDS = 60; const RECORDING_SUMMARY_REQUEST_CACHE_MAX_AGE_SECONDS = 60;
@@ -559,10 +566,10 @@ export class FrigateCameraManagerEngine implements CameraManagerEngine {
continue; continue;
} }
const media = FrigateViewMediaFactory.createRecordingViewMedia( const media = FrigateViewMediaFactory.createRecordingViewMedia(
hass,
recording.cameraID, recording.cameraID,
recording, recording,
cameraConfig, cameraConfig,
this.getCameraMetadata(hass, cameraConfig).title,
); );
if (media) { if (media) {
output.push(media); output.push(media);
@@ -809,4 +816,23 @@ export class FrigateCameraManagerEngine implements CameraManagerEngine {
canFavorite: ViewMediaClassifier.isEvent(media), canFavorite: ViewMediaClassifier.isEvent(media),
}; };
} }
public getCameraMetadata(
hass: HomeAssistant,
cameraConfig: CameraConfig,
): CameraManagerCameraMetadata {
return {
title:
cameraConfig.title ??
getEntityTitle(hass, cameraConfig.camera_entity) ??
getEntityTitle(hass, cameraConfig.webrtc_card?.entity) ??
prettifyTitle(cameraConfig.frigate?.camera_name) ??
cameraConfig.id ??
'',
icon:
cameraConfig?.icon ??
getEntityIcon(hass, cameraConfig.camera_entity) ??
'mdi:video',
};
}
} }
+2 -2
View File
@@ -156,10 +156,10 @@ export class FrigateViewMediaFactory {
} }
static createRecordingViewMedia( static createRecordingViewMedia(
hass: HomeAssistant,
cameraID: string, cameraID: string,
recording: FrigateRecording, recording: FrigateRecording,
cameraConfig: CameraConfig, cameraConfig: CameraConfig,
cameraTitle: string,
): FrigateRecordingViewMedia | null { ): FrigateRecordingViewMedia | null {
if (!cameraConfig.frigate.client_id || !cameraConfig.frigate.camera_name) { if (!cameraConfig.frigate.client_id || !cameraConfig.frigate.camera_name) {
return null; return null;
@@ -175,7 +175,7 @@ export class FrigateViewMediaFactory {
cameraConfig.frigate.camera_name, cameraConfig.frigate.camera_name,
recording, recording,
), ),
getRecordingTitle(hass, cameraConfig, recording), getRecordingTitle(cameraTitle, recording),
); );
} }
} }
+2 -8
View File
@@ -1,8 +1,6 @@
import { HomeAssistant } from 'custom-card-helpers';
import utcToZonedTime from 'date-fns-tz/utcToZonedTime'; import utcToZonedTime from 'date-fns-tz/utcToZonedTime';
import { CameraConfig, ClipsOrSnapshots } from '../../types'; import { CameraConfig, ClipsOrSnapshots } from '../../types';
import { formatDateAndTime, prettifyTitle } from '../../utils/basic'; import { formatDateAndTime, prettifyTitle } from '../../utils/basic';
import { getCameraTitle } from '../../utils/camera';
import { FrigateEvent, FrigateRecording } from './types'; import { FrigateEvent, FrigateRecording } from './types';
/** /**
@@ -24,14 +22,10 @@ export const getEventTitle = (event: FrigateEvent): string => {
}; };
export const getRecordingTitle = ( export const getRecordingTitle = (
hass: HomeAssistant, cameraTitle: string,
cameraConfig: CameraConfig,
recording: FrigateRecording, recording: FrigateRecording,
): string => { ): string => {
const cameraTitle = getCameraTitle(hass, cameraConfig); return `${cameraTitle} ${formatDateAndTime(recording.startTime)}`;
return `${cameraTitle ? `${cameraTitle} ` : ''}${formatDateAndTime(
recording.startTime,
)}`;
}; };
/** /**
+12
View File
@@ -2,6 +2,7 @@ import { HomeAssistant } from 'custom-card-helpers';
import { CameraConfig, CardWideConfig } from '../types.js'; import { CameraConfig, CardWideConfig } from '../types.js';
import { allPromises, arrayify, setify } from '../utils/basic.js'; import { allPromises, arrayify, setify } from '../utils/basic.js';
import { import {
CameraManagerCameraMetadata,
CameraManagerCapabilities, CameraManagerCapabilities,
CameraManagerMediaCapabilities, CameraManagerMediaCapabilities,
DataQuery, DataQuery,
@@ -539,4 +540,15 @@ export class CameraManager {
'desc', 'desc',
); );
} }
public getCameraMetadata(
hass: HomeAssistant,
cameraConfig?: CameraConfig,
): CameraManagerCameraMetadata | null {
const engine = this._engineFactory.getEngineForCamera(cameraConfig);
if (!engine || !cameraConfig) {
return null;
}
return engine.getCameraMetadata(hass, cameraConfig);
}
} }
+5
View File
@@ -85,6 +85,11 @@ export interface CameraManagerMediaCapabilities {
canFavorite: boolean; canFavorite: boolean;
} }
export interface CameraManagerCameraMetadata {
title: string;
icon: string;
}
// =========== // ===========
// Event Query // Event Query
// =========== // ===========
+16 -6
View File
@@ -67,7 +67,7 @@ import {
getActionConfigGivenAction, getActionConfigGivenAction,
} from './utils/action.js'; } from './utils/action.js';
import { contentsChanged, errorToConsole } from './utils/basic.js'; import { contentsChanged, errorToConsole } from './utils/basic.js';
import { getCameraIcon, getCameraID, getCameraTitle } from './utils/camera.js'; import { getCameraID } from './utils/camera.js';
import { import {
getEntityIcon, getEntityIcon,
getEntityTitle, getEntityTitle,
@@ -403,12 +403,16 @@ export class FrigateCard extends LitElement {
const action = createFrigateCardCustomAction('camera_select', { const action = createFrigateCardCustomAction('camera_select', {
camera: camera, camera: camera,
}); });
const metadata = this._hass
? this._cameraManager?.getCameraMetadata(this._hass, config) ?? undefined
: undefined;
return { return {
enabled: true, enabled: true,
icon: getCameraIcon(this._hass, config), icon: metadata?.icon,
entity: config.camera_entity, entity: config.camera_entity,
state_color: true, state_color: true,
title: getCameraTitle(this._hass, config), title: metadata?.title,
selected: this._view?.camera === camera, selected: this._view?.camera === camera,
...(action && { tap_action: action }), ...(action && { tap_action: action }),
}; };
@@ -1104,7 +1108,8 @@ export class FrigateCard extends LitElement {
*/ */
protected willUpdate(changedProps: PropertyValues): void { protected willUpdate(changedProps: PropertyValues): void {
if ( if (
this._cameras && this._cardWideConfig && this._cameras &&
this._cardWideConfig &&
(changedProps.has('_config') || (changedProps.has('_config') ||
changedProps.has('_cameras') || changedProps.has('_cameras') ||
changedProps.has('_cardWideConfig')) changedProps.has('_cardWideConfig'))
@@ -1393,7 +1398,12 @@ export class FrigateCard extends LitElement {
* @returns * @returns
*/ */
protected _mediaPlayerAction(mediaPlayer: string, action: 'play' | 'stop'): void { protected _mediaPlayerAction(mediaPlayer: string, action: 'play' | 'stop'): void {
if (!['play', 'stop'].includes(action) || !this._view) { if (
!['play', 'stop'].includes(action) ||
!this._view ||
!this._hass ||
!this._cameraManager
) {
return; return;
} }
@@ -1417,7 +1427,7 @@ export class FrigateCard extends LitElement {
} else if (this._view?.is('live') && cameraEntity) { } else if (this._view?.is('live') && cameraEntity) {
media_content_id = `media-source://camera/${cameraEntity}`; media_content_id = `media-source://camera/${cameraEntity}`;
media_content_type = 'application/vnd.apple.mpegurl'; media_content_type = 'application/vnd.apple.mpegurl';
title = getCameraTitle(this._hass, cameraConfig); title = this._cameraManager.getCameraMetadata(this._hass, cameraConfig)?.title ?? null;
thumbnail = this._hass?.states[cameraEntity]?.attributes?.entity_picture ?? null; thumbnail = this._hass?.states[cameraEntity]?.attributes?.entity_picture ?? null;
} }
+36 -15
View File
@@ -32,7 +32,6 @@ import {
} from '../../types.js'; } from '../../types.js';
import { stopEventFromActivatingCardWideActions } from '../../utils/action.js'; import { stopEventFromActivatingCardWideActions } from '../../utils/action.js';
import { contentsChanged } from '../../utils/basic.js'; import { contentsChanged } from '../../utils/basic.js';
import { getCameraIcon, getCameraTitle } from '../../utils/camera.js';
import { import {
dispatchExistingMediaLoadedInfoAsEvent, dispatchExistingMediaLoadedInfoAsEvent,
dispatchMediaUnloadedEvent, dispatchMediaUnloadedEvent,
@@ -87,7 +86,7 @@ export const getStateObjOrDispatchError = (
if (stateObj.state === 'unavailable') { if (stateObj.state === 'unavailable') {
dispatchMessageEvent(element, localize('error.live_camera_unavailable'), 'info', { dispatchMessageEvent(element, localize('error.live_camera_unavailable'), 'info', {
icon: 'mdi:connection', icon: 'mdi:connection',
context: getCameraTitle(hass, cameraConfig), context: cameraConfig,
}); });
return null; return null;
} }
@@ -261,6 +260,7 @@ export class FrigateCardLive extends LitElement {
.conditionState=${this.conditionState} .conditionState=${this.conditionState}
.liveOverrides=${this.liveOverrides} .liveOverrides=${this.liveOverrides}
.cardWideConfig=${this.cardWideConfig} .cardWideConfig=${this.cardWideConfig}
.cameraManager=${this.cameraManager}
> >
</frigate-card-live-carousel> </frigate-card-live-carousel>
</frigate-card-surround>`, </frigate-card-surround>`,
@@ -304,6 +304,9 @@ export class FrigateCardLiveCarousel extends LitElement {
@property({ attribute: false }) @property({ attribute: false })
public cardWideConfig?: CardWideConfig; public cardWideConfig?: CardWideConfig;
@property({ attribute: false })
public cameraManager?: CameraManager;
// Index between camera name and slide number. // Index between camera name and slide number.
protected _cameraToSlide: Record<string, number> = {}; protected _cameraToSlide: Record<string, number> = {};
protected _refMediaCarousel: Ref<FrigateCardMediaCarousel> = createRef(); protected _refMediaCarousel: Ref<FrigateCardMediaCarousel> = createRef();
@@ -317,10 +320,7 @@ export class FrigateCardLiveCarousel extends LitElement {
const frigateCardMediaCarousel = this._refMediaCarousel.value; const frigateCardMediaCarousel = this._refMediaCarousel.value;
if ( if (frigateCardMediaCarousel && changedProperties.has('inBackground')) {
frigateCardMediaCarousel &&
changedProperties.has('inBackground')
) {
// If this has changed to be in the background (i.e. preloaded but not // If this has changed to be in the background (i.e. preloaded but not
// visible) take the appropriate play/pause/mute/unmute actions. // visible) take the appropriate play/pause/mute/unmute actions.
if (this.inBackground) { if (this.inBackground) {
@@ -493,7 +493,7 @@ export class FrigateCardLiveCarousel extends LitElement {
cameraConfig: CameraConfig, cameraConfig: CameraConfig,
slideIndex: number, slideIndex: number,
): TemplateResult | void { ): TemplateResult | void {
if (!this.liveConfig) { if (!this.liveConfig || !this.hass || !this.cameraManager) {
return; return;
} }
// The conditionState object contains the currently live camera, which (in // The conditionState object contains the currently live camera, which (in
@@ -510,12 +510,14 @@ export class FrigateCardLiveCarousel extends LitElement {
conditionState, conditionState,
) as LiveConfig; ) as LiveConfig;
const cameraMetadata = this.cameraManager.getCameraMetadata(this.hass, cameraConfig);
return html` return html`
<div class="embla__slide"> <div class="embla__slide">
<frigate-card-live-provider <frigate-card-live-provider
?disabled=${this.liveConfig.lazy_load} ?disabled=${this.liveConfig.lazy_load}
.cameraConfig=${cameraConfig} .cameraConfig=${cameraConfig}
.label=${getCameraTitle(this.hass, cameraConfig)} .label=${cameraMetadata?.title ?? ''}
.liveConfig=${config} .liveConfig=${config}
.hass=${this.hass} .hass=${this.hass}
.cardWideConfig=${this.cardWideConfig} .cardWideConfig=${this.cardWideConfig}
@@ -555,7 +557,14 @@ export class FrigateCardLiveCarousel extends LitElement {
protected render(): TemplateResult | void { protected render(): TemplateResult | void {
const [slides, cameraToSlide] = this._getSlides(); const [slides, cameraToSlide] = this._getSlides();
this._cameraToSlide = cameraToSlide; this._cameraToSlide = cameraToSlide;
if (!slides.length || !this.liveConfig || !this.cameras || !this.view) { if (
!slides.length ||
!this.liveConfig ||
!this.cameras ||
!this.view ||
!this.hass ||
!this.cameraManager
) {
return; return;
} }
@@ -566,7 +575,19 @@ export class FrigateCardLiveCarousel extends LitElement {
) as LiveConfig; ) as LiveConfig;
const [prevID, nextID] = this._getCameraIDsOfNeighbors(); const [prevID, nextID] = this._getCameraIDsOfNeighbors();
const title = getCameraTitle(this.hass, this.cameras.get(this.view.camera));
const cameraMetadataPrevious = prevID ? this.cameraManager.getCameraMetadata(
this.hass,
this.cameras.get(prevID),
) : null;
const cameraMetadataCurrent = this.cameraManager.getCameraMetadata(
this.hass,
this.cameras.get(this.view.camera),
);
const cameraMetadataNext = nextID ? this.cameraManager.getCameraMetadata(
this.hass,
this.cameras.get(nextID),
) : null;
// Notes on the below: // Notes on the below:
// - guard() is used to avoid reseting the carousel unless the // - guard() is used to avoid reseting the carousel unless the
@@ -589,7 +610,7 @@ export class FrigateCardLiveCarousel extends LitElement {
[this.cameras, this.liveConfig], [this.cameras, this.liveConfig],
this._getPlugins.bind(this), this._getPlugins.bind(this),
) as EmblaCarouselPlugins} ) as EmblaCarouselPlugins}
.label="${title ? `${localize('common.live')}: ${title}` : ''}" .label="${cameraMetadataCurrent ? `${localize('common.live')}: ${cameraMetadataCurrent.title}` : ''}"
.titlePopupConfig=${config.controls.title} .titlePopupConfig=${config.controls.title}
.selected=${this._getSelectedCameraIndex()} .selected=${this._getSelectedCameraIndex()}
transitionEffect=${this._getTransitionEffect()} transitionEffect=${this._getTransitionEffect()}
@@ -604,8 +625,8 @@ export class FrigateCardLiveCarousel extends LitElement {
.hass=${this.hass} .hass=${this.hass}
.direction=${'previous'} .direction=${'previous'}
.controlConfig=${config.controls.next_previous} .controlConfig=${config.controls.next_previous}
.label=${getCameraTitle(this.hass, prevID ? this.cameras.get(prevID) : null)} .label=${cameraMetadataPrevious?.title ?? ''}
.icon=${getCameraIcon(this.hass, prevID ? this.cameras.get(prevID) : null)} .icon=${cameraMetadataPrevious?.icon}
?disabled=${prevID === null} ?disabled=${prevID === null}
@click=${(ev) => { @click=${(ev) => {
this._setViewCameraID(prevID); this._setViewCameraID(prevID);
@@ -619,8 +640,8 @@ export class FrigateCardLiveCarousel extends LitElement {
.hass=${this.hass} .hass=${this.hass}
.direction=${'next'} .direction=${'next'}
.controlConfig=${config.controls.next_previous} .controlConfig=${config.controls.next_previous}
.label=${getCameraTitle(this.hass, nextID ? this.cameras.get(nextID) : null)} .label=${cameraMetadataNext?.title ?? ''}
.icon=${getCameraIcon(this.hass, nextID ? this.cameras.get(nextID) : null)} .icon=${cameraMetadataNext?.icon}
?disabled=${nextID === null} ?disabled=${nextID === null}
@click=${(ev) => { @click=${(ev) => {
this._setViewCameraID(nextID); this._setViewCameraID(nextID);
+3 -2
View File
@@ -19,7 +19,6 @@ import { customElement, property } from 'lit/decorators.js';
import { CameraManager } from '../camera/manager'; import { CameraManager } from '../camera/manager';
import { DateRange } from '../camera/range'; import { DateRange } from '../camera/range';
import { CameraConfig, ExtendedHomeAssistant } from '../types'; import { CameraConfig, ExtendedHomeAssistant } from '../types';
import { getCameraTitle } from '../utils/camera';
import { View } from '../view/view'; import { View } from '../view/view';
import { import {
MediaFilterControls, MediaFilterControls,
@@ -193,7 +192,9 @@ export class FrigateCardMediaFilter extends LitElement {
this._cameraOptions = Array.from(this.cameras.entries()).map( this._cameraOptions = Array.from(this.cameras.entries()).map(
([cameraID, cameraConfig]) => ({ ([cameraID, cameraConfig]) => ({
value: cameraID, value: cameraID,
label: getCameraTitle(this.hass, cameraConfig), label: this.hass
? this.cameraManager?.getCameraMetadata(this.hass, cameraConfig)?.title ?? ''
: '',
}), }),
); );
} }
+5 -6
View File
@@ -16,7 +16,6 @@ import thumbnailFeatureRecordingStyle from '../scss/thumbnail-feature-recording.
import thumbnailStyle from '../scss/thumbnail.scss'; import thumbnailStyle from '../scss/thumbnail.scss';
import { stopEventFromActivatingCardWideActions } from '../utils/action.js'; import { stopEventFromActivatingCardWideActions } from '../utils/action.js';
import { errorToConsole, getDurationString, prettifyTitle } from '../utils/basic.js'; import { errorToConsole, getDurationString, prettifyTitle } from '../utils/basic.js';
import { getCameraTitle } from '../utils/camera.js';
import { renderTask } from '../utils/task.js'; import { renderTask } from '../utils/task.js';
import { createFetchThumbnailTask, FetchThumbnailTaskArgs } from '../utils/thumbnail.js'; import { createFetchThumbnailTask, FetchThumbnailTaskArgs } from '../utils/thumbnail.js';
import { View } from '../view/view.js'; import { View } from '../view/view.js';
@@ -270,7 +269,7 @@ export class FrigateCardThumbnail extends LitElement {
* @returns A template to display to the user. * @returns A template to display to the user.
*/ */
protected render(): TemplateResult | void { protected render(): TemplateResult | void {
if (!this.media || !this.cameraConfig) { if (!this.media || !this.cameraConfig || !this.cameraManager || !this.hass) {
return; return;
} }
@@ -295,6 +294,8 @@ export class FrigateCardThumbnail extends LitElement {
this.hass && this.hass &&
this.cameraManager?.getMediaCapabilities(this.media)?.canFavorite; this.cameraManager?.getMediaCapabilities(this.media)?.canFavorite;
const cameraTitle = this.cameraManager.getCameraMetadata(this.hass, this.cameraConfig)?.title;
return html` ${ViewMediaClassifier.isEvent(this.media) return html` ${ViewMediaClassifier.isEvent(this.media)
? html`<frigate-card-thumbnail-feature-event ? html`<frigate-card-thumbnail-feature-event
aria-label="${title ?? ''}" aria-label="${title ?? ''}"
@@ -306,9 +307,7 @@ export class FrigateCardThumbnail extends LitElement {
? html`<frigate-card-thumbnail-feature-recording ? html`<frigate-card-thumbnail-feature-recording
aria-label="${title ?? ''}" aria-label="${title ?? ''}"
title="${title ?? ''}" title="${title ?? ''}"
.cameraTitle=${this.details || !this.cameraConfig || !this.hass .cameraTitle=${this.details ? undefined : cameraTitle}
? undefined
: getCameraTitle(this.hass, this.cameraConfig)}
.date=${this.media.getStartTime() ?? undefined} .date=${this.media.getStartTime() ?? undefined}
></frigate-card-thumbnail-feature-recording>` ></frigate-card-thumbnail-feature-recording>`
: html``} : html``}
@@ -343,7 +342,7 @@ export class FrigateCardThumbnail extends LitElement {
: this.details && ViewMediaClassifier.isRecording(this.media) : this.details && ViewMediaClassifier.isRecording(this.media)
? html`<frigate-card-thumbnail-details-recording ? html`<frigate-card-thumbnail-details-recording
.media=${this.media ?? undefined} .media=${this.media ?? undefined}
.cameraTitle=${getCameraTitle(this.hass, this.cameraConfig)} .cameraTitle=${cameraTitle}
.seek=${this.seek} .seek=${this.seek}
></frigate-card-thumbnail-details-recording>` ></frigate-card-thumbnail-details-recording>`
: html``} : html``}
+10 -4
View File
@@ -42,7 +42,7 @@ import {
dispatchFrigateCardEvent, dispatchFrigateCardEvent,
isHoverableDevice, isHoverableDevice,
} from '../utils/basic'; } from '../utils/basic';
import { getAllDependentCameras, getCameraTitle } from '../utils/camera.js'; import { getAllDependentCameras } from '../utils/camera.js';
import { import {
createViewForEvents, createViewForEvents,
@@ -675,17 +675,23 @@ export class FrigateCardTimelineCore extends LitElement {
this._getTimelineCameraIDs().forEach((cameraID) => { this._getTimelineCameraIDs().forEach((cameraID) => {
const cameraConfig = this.cameras?.get(cameraID); const cameraConfig = this.cameras?.get(cameraID);
if (cameraConfig) { if (!this.hass || !cameraConfig || !this.cameraManager) {
return;
}
const cameraMetadata = this.cameraManager.getCameraMetadata(
this.hass,
cameraConfig,
);
if ( if (
cameraMetadata &&
cameraConfig.frigate.camera_name && cameraConfig.frigate.camera_name &&
cameraConfig.frigate.camera_name !== CAMERA_BIRDSEYE cameraConfig.frigate.camera_name !== CAMERA_BIRDSEYE
) { ) {
groups.push({ groups.push({
id: cameraID, id: cameraID,
content: getCameraTitle(this.hass, cameraConfig), content: cameraMetadata.title,
}); });
} }
}
}); });
return new DataSet(groups); return new DataSet(groups);
} }
+29 -4
View File
@@ -140,10 +140,14 @@ import {
THUMBNAIL_WIDTH_MAX, THUMBNAIL_WIDTH_MAX,
THUMBNAIL_WIDTH_MIN, THUMBNAIL_WIDTH_MIN,
} from './types.js'; } from './types.js';
import { arrayMove } from './utils/basic.js'; import { arrayMove, prettifyTitle } from './utils/basic.js';
import { getCameraID, getCameraTitle } from './utils/camera.js'; import { getCameraID } from './utils/camera.js';
import { FRIGATE_ICON_SVG_PATH } from './camera/frigate/icon.js'; import { FRIGATE_ICON_SVG_PATH } from './camera/frigate/icon.js';
import { getEntitiesFromHASS, sideLoadHomeAssistantElements } from './utils/ha'; import {
getEntitiesFromHASS,
getEntityTitle,
sideLoadHomeAssistantElements,
} from './utils/ha';
import { setLowPerformanceProfile } from './performance.js'; import { setLowPerformanceProfile } from './performance.js';
const MENU_BUTTONS = 'buttons'; const MENU_BUTTONS = 'buttons';
@@ -708,8 +712,29 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
cameraIndex: number, cameraIndex: number,
cameraConfig: RawFrigateCardConfig, cameraConfig: RawFrigateCardConfig,
): string { ): string {
// Attempt to render a recognizable name for the camera, starting with the
// most likely to be useful and working our ways towards the least useful.
// This is only used for the editor since the card itself can use the
// cameraManager.
return ( return (
getCameraTitle(this.hass, cameraConfig) || (typeof cameraConfig?.title === 'string' && cameraConfig.title) ||
(typeof cameraConfig?.camera_entity === 'string'
? getEntityTitle(this.hass, cameraConfig.camera_entity)
: '') ||
(typeof cameraConfig?.webrtc_card === 'object' &&
cameraConfig.webrtc_card &&
typeof cameraConfig.webrtc_card['entity'] === 'string' &&
cameraConfig.webrtc_card['entity']) ||
// Usage of engine specific logic here is allowed as an exception, since
// the camera manager cannot be started with an unparsed and unloaded
// config.
(typeof cameraConfig?.frigate === 'object' &&
cameraConfig.frigate &&
typeof cameraConfig?.frigate['camera_name'] === 'string' &&
cameraConfig.frigate['camera_name']
? prettifyTitle(cameraConfig.frigate['camera_name'])
: '') ||
(typeof cameraConfig?.id === 'string' && cameraConfig.id) ||
localize('editor.camera') + ' #' + cameraIndex localize('editor.camera') + ' #' + cameraIndex
); );
} }
+1
View File
@@ -33,6 +33,7 @@ export function dispatchFrigateCardEvent<T>(
* @returns A prettified name. * @returns A prettified name.
*/ */
export function prettifyTitle(input: string): string; export function prettifyTitle(input: string): string;
export function prettifyTitle(input?: string): string | undefined;
export function prettifyTitle(input?: string): string | undefined { export function prettifyTitle(input?: string): string | undefined {
if (!input) { if (!input) {
return undefined; return undefined;
-50
View File
@@ -1,7 +1,4 @@
import { HomeAssistant } from 'custom-card-helpers';
import { CameraConfig, RawFrigateCardConfig } from '../types.js'; import { CameraConfig, RawFrigateCardConfig } from '../types.js';
import { prettifyTitle } from './basic.js';
import { getEntityIcon, getEntityTitle } from './ha';
/** /**
* Get a camera id. * Get a camera id.
@@ -26,53 +23,6 @@ export function getCameraID(
); );
} }
/**
* Get a camera text title.
* @param hass The Home Assistant object.
* @param config The camera config (either parsed or raw).
* @returns A title string.
*/
export function getCameraTitle(
hass?: HomeAssistant,
config?: CameraConfig | RawFrigateCardConfig | null,
): string {
// Attempt to render a recognizable name for the camera,
// starting with the most likely to be useful and working our
// ways towards the least useful. Extra type checking here since this is also
// used on raw configuration in the editor.
return (
(typeof config?.title === 'string' && config.title) ||
(typeof config?.camera_entity === 'string'
? getEntityTitle(hass, config.camera_entity)
: '') ||
(typeof config?.webrtc_card === 'object' &&
config.webrtc_card &&
typeof config.webrtc_card['entity'] === 'string' &&
config.webrtc_card['entity']) ||
(typeof config?.frigate === 'object' &&
config.frigate &&
typeof config?.frigate['camera_name'] === 'string' &&
config.frigate['camera_name']
? prettifyTitle(config.frigate['camera_name'])
: '') ||
(typeof config?.id === 'string' && config.id) ||
''
);
}
/**
* Get a camera icon.
* @param hass The Home Assistant object.
* @param config The camera config.
* @returns An icon string.
*/
export function getCameraIcon(
hass?: HomeAssistant,
config?: CameraConfig | null,
): string {
return config?.icon || getEntityIcon(hass, config?.camera_entity) || 'mdi:video';
}
/** /**
* Get all cameras that depend on a given camera. * Get all cameras that depend on a given camera.
* @param cameras Cameras map. * @param cameras Cameras map.
+3 -4
View File
@@ -5,7 +5,6 @@ import { dispatchErrorMessageEvent } from '../../components/message.js';
import { localize } from '../../localize/localize.js'; import { localize } from '../../localize/localize.js';
import { import {
BrowseMediaQueryParameters, BrowseMediaQueryParameters,
BrowseRecordingQueryParameters,
CameraConfig, CameraConfig,
ClipsOrSnapshots, ClipsOrSnapshots,
FrigateBrowseMediaSource, FrigateBrowseMediaSource,
@@ -15,7 +14,7 @@ import {
MEDIA_TYPE_PLAYLIST, MEDIA_TYPE_PLAYLIST,
MEDIA_TYPE_VIDEO, MEDIA_TYPE_VIDEO,
} from '../../types.js'; } from '../../types.js';
import { getAllDependentCameras, getCameraTitle } from '../camera.js'; import { getAllDependentCameras } from '../camera.js';
/** /**
* Return the Frigate event_id given a FrigateBrowseMediaSource object. * Return the Frigate event_id given a FrigateBrowseMediaSource object.
@@ -189,7 +188,7 @@ export const mergeFrigateBrowseMediaSources = async (
* @returns A BrowseMediaQueryParameters object. * @returns A BrowseMediaQueryParameters object.
*/ */
export const getBrowseMediaQueryParameters = ( export const getBrowseMediaQueryParameters = (
hass: HomeAssistant, _hass: HomeAssistant,
cameraID: string, cameraID: string,
cameraConfig?: CameraConfig, cameraConfig?: CameraConfig,
overrides?: Partial<BrowseMediaQueryParameters>, overrides?: Partial<BrowseMediaQueryParameters>,
@@ -202,7 +201,7 @@ export const getBrowseMediaQueryParameters = (
cameraName: cameraConfig.frigate.camera_name, cameraName: cameraConfig.frigate.camera_name,
label: cameraConfig.frigate.label, label: cameraConfig.frigate.label,
zone: cameraConfig.frigate.zone, zone: cameraConfig.frigate.zone,
title: getCameraTitle(hass, cameraConfig), title: '', // TODO: Replace: getCameraTitle(hass, cameraConfig),
cameraID: cameraID, cameraID: cameraID,
...overrides, ...overrides,
}; };