diff --git a/src/components/ha-camera-stream.ts b/src/components/ha-camera-stream.ts index c0b5ef6f0e..6ad12fb942 100644 --- a/src/components/ha-camera-stream.ts +++ b/src/components/ha-camera-stream.ts @@ -24,7 +24,7 @@ import "./ha-hls-player"; import "./ha-web-rtc-player"; @customElement("ha-camera-stream") -class HaCameraStream extends LitElement { +export class HaCameraStream extends LitElement { @property({ attribute: false }) public hass?: HomeAssistant; @property({ attribute: false }) public stateObj?: CameraEntity; @@ -81,7 +81,7 @@ class HaCameraStream extends LitElement { return html``; } if (__DEMO__ || this._shouldRenderMJPEG) { - return html` ` : ""} `; @@ -318,6 +320,11 @@ class HaHLSPlayer extends LitElement { this._errorIsFatal = false; } + private _loadedData() { + // @ts-ignore + fireEvent(this, "load"); + } + static get styles(): CSSResultGroup { return css` :host, diff --git a/src/components/ha-web-rtc-player.ts b/src/components/ha-web-rtc-player.ts index 0e7d31bc3a..5de5e0c49e 100644 --- a/src/components/ha-web-rtc-player.ts +++ b/src/components/ha-web-rtc-player.ts @@ -8,6 +8,7 @@ import { } from "lit"; import { customElement, property, state, query } from "lit/decorators"; import { isComponentLoaded } from "../common/config/is_component_loaded"; +import { fireEvent } from "../common/dom/fire_event"; import { handleWebRtcOffer, WebRtcAnswer } from "../data/camera"; import { fetchWebRtcSettings } from "../data/rtsp_to_webrtc"; import type { HomeAssistant } from "../types"; @@ -59,6 +60,7 @@ class HaWebRtcPlayer extends LitElement { ?playsinline=${this.playsInline} ?controls=${this.controls} .poster=${this.posterUrl} + @loadeddata=${this._loadedData} > `; } @@ -188,6 +190,11 @@ class HaWebRtcPlayer extends LitElement { } } + private _loadedData() { + // @ts-ignore + fireEvent(this, "load"); + } + static get styles(): CSSResultGroup { return css` :host, diff --git a/src/panels/lovelace/components/hui-image.ts b/src/panels/lovelace/components/hui-image.ts index fcf1654a0a..555bca93ad 100644 --- a/src/panels/lovelace/components/hui-image.ts +++ b/src/panels/lovelace/components/hui-image.ts @@ -16,6 +16,7 @@ import { CameraEntity, fetchThumbnailUrlWithCache } from "../../../data/camera"; import { UNAVAILABLE } from "../../../data/entity"; import { HomeAssistant } from "../../../types"; import "../../../components/ha-circular-progress"; +import type { HaCameraStream } from "../../../components/ha-camera-stream"; const UPDATE_INTERVAL = 10000; const DEFAULT_FILTER = "grayscale(100%)"; @@ -65,9 +66,9 @@ export class HuiImage extends LitElement { @state() private _loadedImageSrc?: string; - private _intersectionObserver?: IntersectionObserver; + @state() private _lastImageHeight?: number; - private _lastImageHeight?: number; + private _intersectionObserver?: IntersectionObserver; private _cameraUpdater?: number; @@ -192,6 +193,8 @@ export class HuiImage extends LitElement { style=${styleMap({ paddingBottom: useRatio ? `${((100 * this._ratio!.h) / this._ratio!.w).toFixed(2)}%` + : !this._lastImageHeight + ? "56.25%" : undefined, backgroundImage: useRatio && this._loadedImageSrc @@ -203,7 +206,7 @@ export class HuiImage extends LitElement { : undefined, })} class="container ${classMap({ - ratio: useRatio, + ratio: useRatio || !this._lastImageHeight, })}" > ${this.cameraImage && this.cameraView === "live" @@ -212,6 +215,7 @@ export class HuiImage extends LitElement { muted .hass=${this.hass} .stateObj=${cameraObj} + @load=${this._onVideoLoad} > ` : imageSrc === undefined @@ -235,7 +239,7 @@ export class HuiImage extends LitElement { id="brokenImage" style=${styleMap({ height: !useRatio - ? `${this._lastImageHeight || "100"}px` + ? `${this._lastImageHeight}px` || "100%" : undefined, })} >` @@ -245,7 +249,7 @@ export class HuiImage extends LitElement { class="progress-container" style=${styleMap({ height: !useRatio - ? `${this._lastImageHeight || "100"}px` + ? `${this._lastImageHeight}px` || "100%" : undefined, })} > @@ -322,6 +326,13 @@ export class HuiImage extends LitElement { this._lastImageHeight = imgEl.offsetHeight; } + private async _onVideoLoad(ev: Event): Promise { + this._loadState = LoadState.Loaded; + const videoEl = ev.currentTarget as HaCameraStream; + await this.updateComplete; + this._lastImageHeight = videoEl.offsetHeight; + } + private async _updateCameraImageSrcAtInterval(): Promise { // If we hit the interval and it was still loading // it means we timed out so we should show the error.