;
+ /**
+ * Build a working absolute image URL that the browser will not cache.
+ * @param url An input URL (may be relative to document origin)
+ * @returns A new URL (absolute, will not be browser cached).
+ */
+ protected _buildImageURL(url: string): string {
+ const urlObj = new URL(url, document.baseURI);
+ urlObj.searchParams.append('_t', String(Date.now()));
+ return urlObj.toString();
+ }
protected _getImageSource(): string {
- if (this._imageConfig?.src) {
- const url = new URL(this._imageConfig.src);
- url.searchParams.append('t', String(Date.now()));
- return url.toString();
+ if (this._imageConfig?.mode === 'url' && this._imageConfig?.url) {
+ return this._buildImageURL(this._imageConfig.url);
+ } else if (this.hass && this._imageConfig?.mode === 'camera') {
+ const entity =
+ this.cameraConfig?.camera_entity || this.cameraConfig?.webrtc_card?.entity;
+ if (entity) {
+ const state = this.hass.states[entity];
+ if (state && state.attributes.entity_picture) {
+ return this._buildImageURL(state.attributes.entity_picture);
+ }
+ }
}
return defaultImage;
}
diff --git a/src/config-mgmt.ts b/src/config-mgmt.ts
index 5328a22b..e2aec88b 100644
--- a/src/config-mgmt.ts
+++ b/src/config-mgmt.ts
@@ -11,7 +11,7 @@ import {
CONF_EVENT_VIEWER_AUTO_PLAY,
CONF_EVENT_VIEWER_CONTROLS_NEXT_PREVIOUS_SIZE,
CONF_EVENT_VIEWER_CONTROLS_NEXT_PREVIOUS_STYLE,
- CONF_IMAGE_SRC,
+ CONF_IMAGE_URL,
CONF_LIVE_PRELOAD,
CONF_LIVE_WEBRTC_CARD,
CONF_MENU,
@@ -317,7 +317,7 @@ const upgradeToMultipleCameras = (): ((obj: RawFrigateCardConfig) => boolean) =>
* @param key A string key.
* @returns A safe key.
*/
-const updateMenuConditionToMenuOverride = (): ((
+const upgradeMenuConditionToMenuOverride = (): ((
obj: RawFrigateCardConfig,
) => boolean) => {
return function (obj: RawFrigateCardConfig): boolean {
@@ -363,14 +363,14 @@ const UPGRADES = [
upgradeMoveTo('menu_mode', CONF_MENU_MODE),
upgradeMoveTo('menu_buttons', 'menu.buttons'),
upgradeMoveTo('menu_button_size', CONF_MENU_BUTTON_SIZE),
- upgradeMoveTo('image', CONF_IMAGE_SRC, isNotObject),
+ upgradeMoveTo('image', 'image.src', isNotObject),
// v2.0.0 -> v2.1.0
upgradeMoveTo('update_entities', CONF_VIEW_UPDATE_ENTITIES),
// v2.1.0 -> v3.0.0-rc.1
upgradeToMultipleCameras(),
- updateMenuConditionToMenuOverride(),
+ upgradeMenuConditionToMenuOverride(),
upgradeMoveTo('view.timeout', CONF_VIEW_TIMEOUT_SECONDS, toNumberOrIgnore),
upgradeMoveTo('event_viewer.autoplay_clip', CONF_EVENT_VIEWER_AUTO_PLAY),
@@ -383,4 +383,5 @@ const UPGRADES = [
),
upgradeArrayValue(CONF_CAMERAS, upgradeMoveTo('webrtc', 'webrtc_card')),
upgradeMoveToWithOverrides('live.webrtc', CONF_LIVE_WEBRTC_CARD),
+ upgradeMoveToWithOverrides('image.src', CONF_IMAGE_URL),
];
diff --git a/src/const.ts b/src/const.ts
index 0da15d51..f5a998ae 100644
--- a/src/const.ts
+++ b/src/const.ts
@@ -76,8 +76,9 @@ export const CONF_LIVE_TRANSITION_EFFECT = `${CONF_LIVE}.transition_effect` as c
export const CONF_LIVE_WEBRTC_CARD = `${CONF_LIVE}.webrtc_card` as const;
export const CONF_IMAGE = 'image' as const;
+export const CONF_IMAGE_MODE = `${CONF_IMAGE}.mode` as const;
export const CONF_IMAGE_REFRESH_SECONDS = `${CONF_IMAGE}.refresh_seconds` as const;
-export const CONF_IMAGE_SRC = `${CONF_IMAGE}.src` as const;
+export const CONF_IMAGE_URL = `${CONF_IMAGE}.url` as const;
export const CONF_MENU = 'menu' as const;
export const CONF_MENU_BUTTONS_FRIGATE = `${CONF_MENU}.buttons.frigate` as const;
diff --git a/src/editor.ts b/src/editor.ts
index 90f0e9d6..3b6b02e6 100644
--- a/src/editor.ts
+++ b/src/editor.ts
@@ -39,8 +39,9 @@ import {
CONF_EVENT_VIEWER_DRAGGABLE,
CONF_EVENT_VIEWER_LAZY_LOAD,
CONF_EVENT_VIEWER_TRANSITION_EFFECT,
+ CONF_IMAGE_MODE,
CONF_IMAGE_REFRESH_SECONDS,
- CONF_IMAGE_SRC,
+ CONF_IMAGE_URL,
CONF_LIVE_AUTO_UNMUTE,
CONF_LIVE_CONTROLS_NEXT_PREVIOUS_SIZE,
CONF_LIVE_CONTROLS_NEXT_PREVIOUS_STYLE,
@@ -274,6 +275,13 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
['slide', localize('config.event_viewer.transition_effects.slide')],
]);
+ protected _imageModes = new Map([
+ ['', ''],
+ ['camera', localize('config.image.modes.camera')],
+ ['screensaver', localize('config.image.modes.screensaver')],
+ ['url', localize('config.image.modes.url')],
+ ]);
+
public setConfig(config: RawFrigateCardConfig): void {
// Note: This does not use Zod to parse the configuration, so it may be
// partially or completely invalid. It's more useful to have a partially
@@ -882,7 +890,11 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
${this._renderOptionSetHeader('image')}
${options.image.show
? html`
- ${this._renderStringInput(CONF_IMAGE_SRC)}
+ ${this._renderDropdown(
+ CONF_IMAGE_MODE,
+ this._imageModes,
+ )}
+ ${this._renderStringInput(CONF_IMAGE_URL)}
${this._renderStringInput(CONF_IMAGE_REFRESH_SECONDS, 'number')}
`
: ''}
diff --git a/src/localize/languages/en.json b/src/localize/languages/en.json
index ca94e5c9..36e3b3f0 100644
--- a/src/localize/languages/en.json
+++ b/src/localize/languages/en.json
@@ -127,7 +127,13 @@
}
},
"image": {
- "src": "Static image URL/data for image view",
+ "mode": "Image view mode",
+ "modes": {
+ "screensaver": "Embedded Frigate logo",
+ "url": "Arbitrary image specified by URL",
+ "camera": "Home Assistant camera snapshot of camera entity"
+ },
+ "url": "Static image URL for image view",
"refresh_seconds": "Number of seconds after which to refresh (0=never)"
},
"menu": {
diff --git a/src/types.ts b/src/types.ts
index c051ea79..1aea91e3 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -404,12 +404,19 @@ const viewConfigSchema = z
* Image view configuration section.
*/
+export const IMAGE_MODES = [
+ 'screensaver',
+ 'camera',
+ 'url',
+] as const;
const imageConfigDefault = {
+ mode: 'url' as const,
refresh_seconds: 0,
};
const imageConfigSchema = z
.object({
- src: z.string().optional(),
+ mode: z.enum(IMAGE_MODES).default(imageConfigDefault.mode),
+ url: z.string().optional(),
refresh_seconds: z.number().min(0).default(imageConfigDefault.refresh_seconds),
})
.merge(actionsSchema)