Move camera name and icon into camera manager.
This commit is contained in:
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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',
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
|
||||||
)}`;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -85,6 +85,11 @@ export interface CameraManagerMediaCapabilities {
|
|||||||
canFavorite: boolean;
|
canFavorite: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface CameraManagerCameraMetadata {
|
||||||
|
title: string;
|
||||||
|
icon: string;
|
||||||
|
}
|
||||||
|
|
||||||
// ===========
|
// ===========
|
||||||
// Event Query
|
// Event Query
|
||||||
// ===========
|
// ===========
|
||||||
|
|||||||
+16
-6
@@ -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
@@ -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);
|
||||||
|
|||||||
@@ -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 ?? ''
|
||||||
|
: '',
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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``}
|
||||||
|
|||||||
@@ -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,16 +675,22 @@ 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) {
|
||||||
if (
|
return;
|
||||||
cameraConfig.frigate.camera_name &&
|
}
|
||||||
cameraConfig.frigate.camera_name !== CAMERA_BIRDSEYE
|
const cameraMetadata = this.cameraManager.getCameraMetadata(
|
||||||
) {
|
this.hass,
|
||||||
groups.push({
|
cameraConfig,
|
||||||
id: cameraID,
|
);
|
||||||
content: getCameraTitle(this.hass, cameraConfig),
|
if (
|
||||||
});
|
cameraMetadata &&
|
||||||
}
|
cameraConfig.frigate.camera_name &&
|
||||||
|
cameraConfig.frigate.camera_name !== CAMERA_BIRDSEYE
|
||||||
|
) {
|
||||||
|
groups.push({
|
||||||
|
id: cameraID,
|
||||||
|
content: cameraMetadata.title,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return new DataSet(groups);
|
return new DataSet(groups);
|
||||||
|
|||||||
+29
-4
@@ -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
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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,
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user