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.