diff --git a/src/card.ts b/src/card.ts index 18909c92..8d33002b 100644 --- a/src/card.ts +++ b/src/card.ts @@ -36,7 +36,12 @@ import type { Message, } from './types.js'; -import { CAMERA_BIRDSEYE, CARD_VERSION, MEDIA_PLAYER_SUPPORT_BROWSE_MEDIA, REPO_URL } from './const.js'; +import { + CAMERA_BIRDSEYE, + CARD_VERSION, + MEDIA_PLAYER_SUPPORT_BROWSE_MEDIA, + REPO_URL, +} from './const.js'; import { FrigateCardElements } from './components/elements.js'; import { FrigateCardImage } from './components/image.js'; import { FRIGATE_BUTTON_MENU_ICON, FrigateCardMenu } from './components/menu.js'; @@ -428,14 +433,23 @@ export class FrigateCard extends LitElement { tap_action: createFrigateCardCustomAction('image') as FrigateCardCustomAction, }); - buttons.push({ - icon: 'mdi:chart-gantt', - ...this._getConfig().menu.buttons.timeline, - type: 'custom:frigate-card-menu-icon', - title: localize('config.view.views.timeline'), - style: this._view?.is('timeline') ? this._getEmphasizedStyle() : {}, - tap_action: createFrigateCardCustomAction('timeline') as FrigateCardCustomAction, - }); + // Don't show the timeline button unless there's at least one non-birdseye + // camera with a Frigate camera name. + if ( + this._cameras && + [...this._cameras.values()].some( + (config) => config.camera_name && config.camera_name !== CAMERA_BIRDSEYE, + ) + ) { + buttons.push({ + icon: 'mdi:chart-gantt', + ...this._getConfig().menu.buttons.timeline, + type: 'custom:frigate-card-menu-icon', + title: localize('config.view.views.timeline'), + style: this._view?.is('timeline') ? this._getEmphasizedStyle() : {}, + tap_action: createFrigateCardCustomAction('timeline') as FrigateCardCustomAction, + }); + } if ( this._view?.isViewerView() || @@ -484,9 +498,11 @@ export class FrigateCard extends LitElement { } } return false; - } + }; - const mediaPlayers = Object.keys(this._hass?.states || {}).filter(isValidMediaPlayer); + const mediaPlayers = Object.keys(this._hass?.states || {}).filter( + isValidMediaPlayer, + ); if ( mediaPlayers.length && (this._view?.isViewerView() || @@ -843,7 +859,7 @@ export class FrigateCard extends LitElement { if (success) { this._initialized = true; } - }) + }); } } diff --git a/src/components/timeline.ts b/src/components/timeline.ts index 3d8ad9ff..108aa6f7 100644 --- a/src/components/timeline.ts +++ b/src/components/timeline.ts @@ -37,9 +37,11 @@ import { View, ViewContext } from '../view'; import { dispatchErrorMessageEvent, dispatchFrigateCardEvent, + dispatchMessageEvent, getCameraTitle, stopEventFromActivatingCardWideActions, } from '../common.js'; +import { localize } from '../localize/localize'; import timelineCoreStyle from '../scss/timeline-core.scss'; import timelineStyle from '../scss/timeline.scss'; @@ -593,7 +595,7 @@ export class FrigateCardTimelineCore extends LitElement { protected _getGroups(): DataGroupCollectionType { const groups: FrigateCardGroupData[] = []; this.cameras?.forEach((cameraConfig, camera) => { - if (cameraConfig.camera_name !== CAMERA_BIRDSEYE) { + if (cameraConfig.camera_name && cameraConfig.camera_name !== CAMERA_BIRDSEYE) { groups.push({ id: camera, content: getCameraTitle(this.hass, cameraConfig), @@ -882,10 +884,21 @@ export class FrigateCardTimelineCore extends LitElement { if (this._timeline) { this._timeline.setOptions(options); } else { + // Don't show an empty timeline, show a message instead. + const groups = this._getGroups(); + if (!groups.length) { + dispatchMessageEvent( + this, + localize('error.timeline_no_cameras'), + 'mdi:chart-gantt', + ); + return; + } + this._timeline = new Timeline( this._refTimeline.value, this._events.dataset, - this._getGroups(), + groups, options, ); this._timeline.on('select', this._timelineSelectHandler.bind(this)); diff --git a/src/localize/languages/en.json b/src/localize/languages/en.json index b9223c0e..3ef5eefc 100644 --- a/src/localize/languages/en.json +++ b/src/localize/languages/en.json @@ -312,6 +312,7 @@ "no_camera_name": "Could not determine a Frigate camera name for camera (or one of its dependents), please specify either 'camera_entity' or 'camera_name'", "download_no_media": "No media to download", "download_no_event_id": "Could not extract Frigate event id from media", - "download_sign_failed": "Could not sign media URL for download" + "download_sign_failed": "Could not sign media URL for download", + "timeline_no_cameras": "No Frigate cameras to show in timeline" } }