diff --git a/src/common/util/time-cache-function-promise.ts b/src/common/util/time-cache-function-promise.ts index c11a901884..d841ea256b 100644 --- a/src/common/util/time-cache-function-promise.ts +++ b/src/common/util/time-cache-function-promise.ts @@ -7,14 +7,10 @@ interface ResultCache { export const timeCachePromiseFunc = async ( cacheKey: string, cacheTime: number, - func: ( - hass: HomeAssistant, - entityId: string, - ...args: unknown[] - ) => Promise, + func: (hass: HomeAssistant, entityId: string, ...args: any[]) => Promise, hass: HomeAssistant, entityId: string, - ...args: unknown[] + ...args: any[] ): Promise => { let cache: ResultCache | undefined = (hass as any)[cacheKey]; diff --git a/src/data/camera.ts b/src/data/camera.ts index 8e93b45e09..0927e50e53 100644 --- a/src/data/camera.ts +++ b/src/data/camera.ts @@ -38,22 +38,28 @@ export const computeMJPEGStreamUrl = (entity: CameraEntity) => export const fetchThumbnailUrlWithCache = ( hass: HomeAssistant, - entityId: string + entityId: string, + width: number, + height: number ) => timeCachePromiseFunc( "_cameraTmbUrl", 9000, fetchThumbnailUrl, hass, - entityId + entityId, + width, + height ); export const fetchThumbnailUrl = async ( hass: HomeAssistant, - entityId: string + entityId: string, + width: number, + height: number ) => { const path = await getSignedPath(hass, `/api/camera_proxy/${entityId}`); - return hass.hassUrl(path.path); + return hass.hassUrl(`${path.path}&width=${width}&height=${height}`); }; export const fetchStreamUrl = async ( diff --git a/src/panels/lovelace/components/hui-image.ts b/src/panels/lovelace/components/hui-image.ts index 70f15fea22..e10366a7e5 100644 --- a/src/panels/lovelace/components/hui-image.ts +++ b/src/panels/lovelace/components/hui-image.ts @@ -19,6 +19,10 @@ import { HomeAssistant } from "../../../types"; const UPDATE_INTERVAL = 10000; const DEFAULT_FILTER = "grayscale(100%)"; +const MAX_IMAGE_WIDTH = 640; +const ASPECT_RATIO_DEFAULT = 9 / 16; +const SCALING_FACTOR = 2; + export interface StateSpecificConfig { [state: string]: string; } @@ -228,9 +232,25 @@ export class HuiImage extends LitElement { return; } + // One the first render we will not know the width + const element_width = this._image.offsetWidth + ? this._image.offsetWidth + : MAX_IMAGE_WIDTH; + // Because the aspect ratio might result in a smaller image, + // we ask for 200% of what we need to make sure the image is + // still clear. In practice, for 4k sources, this is still + // an order of magnitude smaller. + const width = Math.ceil(element_width * SCALING_FACTOR); + // If the image has not rendered yet we may have a zero height + const height = this._image.offsetHeight + ? this._image.offsetHeight * SCALING_FACTOR + : Math.ceil(element_width * SCALING_FACTOR * ASPECT_RATIO_DEFAULT); + this._cameraImageSrc = await fetchThumbnailUrlWithCache( this.hass, - this.cameraImage + this.cameraImage, + width, + height ); }