diff --git a/src/components/ha-camera-stream.ts b/src/components/ha-camera-stream.ts index 8c50c15934..c0b5ef6f0e 100644 --- a/src/components/ha-camera-stream.ts +++ b/src/components/ha-camera-stream.ts @@ -15,6 +15,7 @@ import { CAMERA_SUPPORT_STREAM, computeMJPEGStreamUrl, fetchStreamUrl, + fetchThumbnailUrlWithCache, STREAM_TYPE_HLS, STREAM_TYPE_WEB_RTC, } from "../data/camera"; @@ -37,6 +38,9 @@ class HaCameraStream extends LitElement { @property({ type: Boolean, attribute: "allow-exoplayer" }) public allowExoPlayer = false; + // Video background image before its loaded + @state() private _posterUrl?: string; + // We keep track if we should force MJPEG if there was a failure // to get the HLS stream url. This is reset if we change entities. @state() private _forceMJPEG?: string; @@ -51,12 +55,14 @@ class HaCameraStream extends LitElement { !this._shouldRenderMJPEG && this.stateObj && (changedProps.get("stateObj") as CameraEntity | undefined)?.entity_id !== - this.stateObj.entity_id && - this.stateObj!.attributes.frontend_stream_type === STREAM_TYPE_HLS + this.stateObj.entity_id ) { - this._forceMJPEG = undefined; - this._url = undefined; - this._getStreamUrl(); + this._getPosterUrl(); + if (this.stateObj!.attributes.frontend_stream_type === STREAM_TYPE_HLS) { + this._forceMJPEG = undefined; + this._url = undefined; + this._getStreamUrl(); + } } } @@ -94,6 +100,7 @@ class HaCameraStream extends LitElement { .controls=${this.controls} .hass=${this.hass} .url=${this._url} + .posterUrl=${this._posterUrl} >` : html``; } @@ -105,6 +112,7 @@ class HaCameraStream extends LitElement { .controls=${this.controls} .hass=${this.hass} .entityid=${this.stateObj.entity_id} + .posterUrl=${this._posterUrl} >`; } return html``; @@ -129,6 +137,20 @@ class HaCameraStream extends LitElement { return !isComponentLoaded(this.hass!, "stream"); } + private async _getPosterUrl(): Promise { + try { + this._posterUrl = await fetchThumbnailUrlWithCache( + this.hass!, + this.stateObj!.entity_id, + this.clientWidth, + this.clientHeight + ); + } catch (err: any) { + // poster url is optional + this._posterUrl = undefined; + } + } + private async _getStreamUrl(): Promise { try { const { url } = await fetchStreamUrl( diff --git a/src/components/ha-hls-player.ts b/src/components/ha-hls-player.ts index 70a91b7e5e..895bba4d18 100644 --- a/src/components/ha-hls-player.ts +++ b/src/components/ha-hls-player.ts @@ -23,6 +23,8 @@ class HaHLSPlayer extends LitElement { @property() public url!: string; + @property() public posterUrl!: string; + @property({ type: Boolean, attribute: "controls" }) public controls = false; @@ -78,6 +80,7 @@ class HaHLSPlayer extends LitElement { : ""} ${!this._errorIsFatal ? html` `; }