Merge pull request #1518 from dermotduffy/entity-picture
Add support for arbitrary image entities
This commit is contained in:
@@ -356,7 +356,11 @@ cameras:
|
||||
- camera_entity: camera.back_yard
|
||||
live_provider: image
|
||||
image:
|
||||
mode: auto
|
||||
refresh_seconds: 1
|
||||
url: 'https://path/to/image.png'
|
||||
entity: image.office_person
|
||||
entity_parameters: 'width=400&height=200'
|
||||
- camera_entity: camera.office_motioneye
|
||||
motioneye:
|
||||
images:
|
||||
|
||||
@@ -44,10 +44,13 @@ cameras:
|
||||
# [...]
|
||||
```
|
||||
|
||||
| Option | Default | Description |
|
||||
| ----------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `refresh_seconds` | 1 | The image will be refreshed at least every `refresh_seconds`. `0` implies no refreshing. |
|
||||
| `url` | | **Advanced**: A static image URL to be fetched in lieu of the Home Assistant image for the given camera. This may be useful for advanced configurations where the camera image is being provided by some non-Home Assistant system. This will also set the temporary loading image used when `show_image_during_load` is set to true under the `live` configuration. |
|
||||
| Option | Default | Description |
|
||||
| ------------------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `entity` | | The entity to use when `mode` is set to `entity`. This entity is expected to have an `entity_picture` attribute that specifies the image URL. |
|
||||
| `entity_parameters` | | Optional URL parameters to add to the URL generated for entity-based modes (i.e. when `mode` is `camera` or `entity`). |
|
||||
| `mode` | `auto` | Value must be one of `url` (to fetch an arbitrary image URL), `camera` (to show a still of the currently selected camera entity using either `camera_entity` or `webrtc_card.entity` in that order of precedence), `entity` to show an image associated with a named entity (see the `entity` parameter below), or `screensaver` (to show an [embedded stock Frigate card logo](https://github.com/dermotduffy/frigate-hass-card/blob/main/src/images/frigate-bird-in-sky.jpg)). If `auto`, the mode is chosen automatically based on whether `url` or `entity` parameters have been specified. |
|
||||
| `refresh_seconds` | 1 | The image will be refreshed at least every `refresh_seconds` (it may refresh more frequently, e.g. whenever Home Assistant updates its camera security token). `0` implies no refreshing. |
|
||||
| `url` | | A static image URL to be used when the `mode` is set to `url` or when a temporary image is required (e.g. may appear momentarily prior to load of a camera snapshot in the `camera` mode). Note that a `_t=[timestsamp]` query parameter will be automatically added to all URLs such that the image will not be cached by the browser. |
|
||||
|
||||
## `jsmpeg`
|
||||
|
||||
|
||||
@@ -7,12 +7,14 @@ image:
|
||||
# [...]
|
||||
```
|
||||
|
||||
| Option | Default | Description |
|
||||
| ----------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `actions` | | [Actions](actions/README.md) to use for the `image` view. |
|
||||
| `mode` | `url` | Mode of the the `image` view. Value must be one of `url` (to fetch an arbitrary image URL), `camera` (to show a still of the currently selected camera using either `camera_entity` or `webrtc_card.entity` in that order of precedence), or `screensaver` (to show an [embedded stock Frigate card logo](https://github.com/dermotduffy/frigate-hass-card/blob/main/src/images/frigate-bird-in-sky.jpg)). In either `url` or `camera` mode, the `screensaver` content is used as a fallback if a URL is not specified or cannot be derived. |
|
||||
| `refresh_seconds` | 0 | The image will be refreshed at least every `refresh_seconds` (it may refresh more frequently, e.g. whenever Home Assistant updates its camera security token). `0` implies no refreshing. |
|
||||
| `url` | | A static image URL to be used when the `mode` is set to `url` or when a temporary image is required (e.g. may appear momentarily prior to load of a camera snapshot in the `camera` mode). Note that a `_t=[timestsamp]` query parameter will be automatically added to all URLs such that the image will not be cached by the browser. |
|
||||
| Option | Default | Description |
|
||||
| ------------------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `actions` | | [Actions](actions/README.md) to use for the `image` view. |
|
||||
| `entity` | | The entity to use when `mode` is set to `entity`. This entity is expected to have an `entity_picture` attribute that specifies the image URL. |
|
||||
| `entity_parameters` | | Optional URL parameters to add to the URL generated for entity-based modes (i.e. when `mode` is `camera` or `entity`). |
|
||||
| `mode` | `auto` | Value must be one of `url` (to fetch an arbitrary image URL), `camera` (to show a still of the currently selected camera entity using either `camera_entity` or `webrtc_card.entity` in that order of precedence), `entity` to show an image associated with a named entity (see the `entity` parameter below), or `screensaver` (to show an [embedded stock Frigate card logo](https://github.com/dermotduffy/frigate-hass-card/blob/main/src/images/frigate-bird-in-sky.jpg)). If `auto`, the mode is chosen automatically based on whether `url` or `entity` parameters have been specified. |
|
||||
| `refresh_seconds` | 1 | The image will be refreshed at least every `refresh_seconds` (it may refresh more frequently, e.g. whenever Home Assistant updates its camera security token). `0` implies no refreshing. |
|
||||
| `url` | | A static image URL to be used when the `mode` is set to `url` or when a temporary image is required (e.g. may appear momentarily prior to load of a camera snapshot in the `camera` mode). Note that a `_t=[timestsamp]` query parameter will be automatically added to all URLs such that the image will not be cached by the browser. |
|
||||
|
||||
?> When `mode` is set to `camera` this is effectively providing the same image as the `image` [live provider](cameras/live-provider.md) would show in the live camera carousel.
|
||||
|
||||
@@ -22,8 +24,11 @@ image:
|
||||
|
||||
```yaml
|
||||
image:
|
||||
mode: url
|
||||
refresh_seconds: 0
|
||||
mode: auto
|
||||
refresh_seconds: 1
|
||||
url: 'https://path/to/image.png'
|
||||
entity: image.office_person
|
||||
entity_parameters: 'width=400&height=200'
|
||||
actions:
|
||||
entity: light.office_main_lights
|
||||
tap_action:
|
||||
|
||||
@@ -48,7 +48,7 @@ export class StatusBarItemManager {
|
||||
const engineLogoIcon = cameraMetadata?.engineLogo ?? null;
|
||||
const title = options?.view?.is('live')
|
||||
? cameraMetadata?.title ?? null
|
||||
: options?.view?.isAnyMediaView()
|
||||
: options?.view?.isViewerView()
|
||||
? options?.view.queryResults?.getSelectedResult()?.getTitle() ?? null
|
||||
: null;
|
||||
const resolution = options?.mediaLoadedInfo
|
||||
|
||||
+81
-27
@@ -12,8 +12,9 @@ import { customElement, property } from 'lit/decorators.js';
|
||||
import { live } from 'lit/directives/live.js';
|
||||
import { createRef, ref, Ref } from 'lit/directives/ref.js';
|
||||
import isEqual from 'lodash-es/isEqual';
|
||||
import { CameraManager } from '../camera-manager/manager.js';
|
||||
import { CachedValueController } from '../components-lib/cached-value-controller.js';
|
||||
import { CameraConfig, ImageViewConfig } from '../config/types.js';
|
||||
import { CameraConfig, ImageMode, ImageViewConfig } from '../config/types.js';
|
||||
import defaultImage from '../images/frigate-bird-in-sky.jpg';
|
||||
import { localize } from '../localize/localize.js';
|
||||
import imageStyle from '../scss/image.scss';
|
||||
@@ -28,7 +29,6 @@ import {
|
||||
} from '../utils/media-info.js';
|
||||
import { View } from '../view/view.js';
|
||||
import { dispatchErrorMessageEvent } from './message.js';
|
||||
import { CameraManager } from '../camera-manager/manager.js';
|
||||
|
||||
// See TOKEN_CHANGE_INTERVAL in https://github.com/home-assistant/core/blob/dev/homeassistant/components/camera/__init__.py .
|
||||
const HASS_REJECTION_CUTOFF_MS = 5 * 60 * 1000;
|
||||
@@ -119,14 +119,12 @@ export class FrigateCardImage extends LitElement implements FrigateCardMediaPlay
|
||||
return false;
|
||||
}
|
||||
|
||||
const cameraEntity = this._getCameraEntity();
|
||||
if (
|
||||
changedProps.has('hass') &&
|
||||
changedProps.size == 1 &&
|
||||
this.imageConfig?.mode === 'camera' &&
|
||||
cameraEntity
|
||||
) {
|
||||
if (isHassDifferent(this.hass, changedProps.get('hass'), [cameraEntity])) {
|
||||
const relevantEntity = this._getRelevantEntityForMode(
|
||||
this._resolveMode(this.imageConfig?.mode),
|
||||
);
|
||||
|
||||
if (changedProps.has('hass') && changedProps.size == 1 && relevantEntity) {
|
||||
if (isHassDifferent(this.hass, changedProps.get('hass'), [relevantEntity])) {
|
||||
// If the state of the camera entity has changed, remove the cached
|
||||
// value (will be re-calculated in willUpdate). This is important to
|
||||
// ensure a changed access token is immediately used.
|
||||
@@ -158,6 +156,10 @@ export class FrigateCardImage extends LitElement implements FrigateCardMediaPlay
|
||||
}
|
||||
}
|
||||
|
||||
const relevantEntity = this._getRelevantEntityForMode(
|
||||
this._resolveMode(this.imageConfig?.mode),
|
||||
);
|
||||
|
||||
// If the camera or view changed, immediately discard the old value (view to
|
||||
// allow pressing of the image button to fetch a fresh image). Likewise, if
|
||||
// the state is not acceptable, discard the old value (to allow a stock or
|
||||
@@ -165,8 +167,7 @@ export class FrigateCardImage extends LitElement implements FrigateCardMediaPlay
|
||||
if (
|
||||
changedProps.has('cameraConfig') ||
|
||||
changedProps.has('view') ||
|
||||
(this.imageConfig?.mode === 'camera' &&
|
||||
!this._getAcceptableState(this._getCameraEntity()))
|
||||
(relevantEntity && !this._getAcceptableState(relevantEntity))
|
||||
) {
|
||||
this._cachedValueController?.clearValue();
|
||||
}
|
||||
@@ -245,24 +246,75 @@ export class FrigateCardImage extends LitElement implements FrigateCardMediaPlay
|
||||
/**
|
||||
* 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).
|
||||
* @returns A new URL as a string (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 _buildImageURL(url: URL): string {
|
||||
url.searchParams.append('_t', String(Date.now()));
|
||||
return url.toString();
|
||||
}
|
||||
|
||||
protected _addQueryParametersToURL(url: URL, parameters?: string): URL {
|
||||
if (parameters) {
|
||||
const searchParams = new URLSearchParams(parameters);
|
||||
for (const [key, value] of searchParams.entries()) {
|
||||
url.searchParams.append(key, value);
|
||||
}
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
protected _getRelevantEntityForMode(mode: Exclude<ImageMode, 'auto'>): string | null {
|
||||
return mode === 'camera'
|
||||
? this._getCameraEntity()
|
||||
: mode === 'entity'
|
||||
? this.imageConfig?.entity ?? null
|
||||
: null;
|
||||
}
|
||||
|
||||
protected _resolveMode(mode?: ImageMode): Exclude<ImageMode, 'auto'> {
|
||||
if (!mode) {
|
||||
return 'screensaver';
|
||||
} else if (mode !== 'auto') {
|
||||
return mode;
|
||||
}
|
||||
|
||||
const cameraEntity = this._getCameraEntity();
|
||||
if (this.imageConfig?.entity) {
|
||||
return 'entity';
|
||||
} else if (this.imageConfig?.url) {
|
||||
return 'url';
|
||||
} else if (cameraEntity) {
|
||||
return 'camera';
|
||||
}
|
||||
|
||||
return 'screensaver';
|
||||
}
|
||||
|
||||
protected _getImageSource(): string {
|
||||
if (this.hass && this.imageConfig?.mode === 'camera') {
|
||||
const mode = this._resolveMode(this.imageConfig?.mode);
|
||||
|
||||
if (this.hass && mode === 'camera') {
|
||||
const state = this._getAcceptableState(this._getCameraEntity());
|
||||
if (state?.attributes.entity_picture) {
|
||||
return this._buildImageURL(state.attributes.entity_picture);
|
||||
const urlObj = new URL(state.attributes.entity_picture, document.baseURI);
|
||||
this._addQueryParametersToURL(urlObj, this.imageConfig?.entity_parameters);
|
||||
return this._buildImageURL(urlObj);
|
||||
}
|
||||
}
|
||||
if (this.imageConfig?.mode !== 'screensaver' && this.imageConfig?.url) {
|
||||
return this._buildImageURL(this.imageConfig.url);
|
||||
|
||||
if (this.hass && mode === 'entity' && this.imageConfig?.entity) {
|
||||
const state = this._getAcceptableState(this.imageConfig?.entity);
|
||||
if (state?.attributes.entity_picture) {
|
||||
const urlObj = new URL(state.attributes.entity_picture, document.baseURI);
|
||||
this._addQueryParametersToURL(urlObj, this.imageConfig?.entity_parameters);
|
||||
return this._buildImageURL(urlObj);
|
||||
}
|
||||
}
|
||||
|
||||
if (mode === 'url' && this.imageConfig?.url) {
|
||||
return this._buildImageURL(new URL(this.imageConfig.url, document.baseURI));
|
||||
}
|
||||
|
||||
return defaultImage;
|
||||
}
|
||||
|
||||
@@ -300,13 +352,15 @@ export class FrigateCardImage extends LitElement implements FrigateCardMediaPlay
|
||||
}
|
||||
}}
|
||||
@error=${() => {
|
||||
if (this.imageConfig?.mode === 'camera') {
|
||||
// In camera mode, the user has likely not made an error, but HA
|
||||
// may be unavailble, so show the stock image. Don't let the URL
|
||||
// override the stock image in this case, as this could create an
|
||||
// error loop if that URL subsequently failed to load.
|
||||
const mode = this._resolveMode(this.imageConfig?.mode);
|
||||
if (mode === 'camera' || mode === 'entity') {
|
||||
// In camera or entity mode, the user has likely not made an
|
||||
// error, but HA may be unavailble, so show the stock image.
|
||||
// Don't let the URL override the stock image in this case, as
|
||||
// this could create an error loop if that URL subsequently
|
||||
// failed to load.
|
||||
this._forceSafeImage(true);
|
||||
} else if (this.imageConfig?.mode === 'url') {
|
||||
} else if (mode === 'url') {
|
||||
// In url mode, the user likely specified a URL that cannot be
|
||||
// resolved. Show an error message.
|
||||
dispatchErrorMessageEvent(this, localize('error.image_load_error'), {
|
||||
|
||||
@@ -64,15 +64,8 @@ export class FrigateCardLiveImage extends LitElement implements FrigateCardMedia
|
||||
return html`
|
||||
<frigate-card-image
|
||||
${ref(this._refImage)}
|
||||
.imageConfig=${{
|
||||
mode: this.cameraConfig.image.url ? ('url' as const) : ('camera' as const),
|
||||
refresh_seconds: this.cameraConfig.image.refresh_seconds,
|
||||
url: this.cameraConfig.image.url,
|
||||
|
||||
// The live provider will take care of zoom and layout options.
|
||||
zoomable: false,
|
||||
}}
|
||||
.hass=${this.hass}
|
||||
.imageConfig=${this.cameraConfig.image}
|
||||
.cameraConfig=${this.cameraConfig}
|
||||
>
|
||||
</frigate-card-image>
|
||||
|
||||
+12
-17
@@ -917,25 +917,25 @@ export type PTZControlsConfig = z.infer<typeof ptzControlsConfigSchema>;
|
||||
// `image` card view.
|
||||
// *************************************************************************
|
||||
|
||||
const imageBaseConfigDefault = {
|
||||
const imageConfigDefault = {
|
||||
mode: 'auto' as const,
|
||||
refresh_seconds: 1,
|
||||
};
|
||||
|
||||
const IMAGE_MODES = ['auto', 'camera', 'entity', 'screensaver', 'url'] as const;
|
||||
export type ImageMode = (typeof IMAGE_MODES)[number];
|
||||
|
||||
const imageBaseConfigSchema = z.object({
|
||||
mode: z.enum(IMAGE_MODES).default(imageConfigDefault.mode),
|
||||
|
||||
refresh_seconds: z.number().min(0).default(imageConfigDefault.refresh_seconds),
|
||||
|
||||
url: z.string().optional(),
|
||||
refresh_seconds: z.number().min(0).default(imageBaseConfigDefault.refresh_seconds),
|
||||
entity: z.string().optional(),
|
||||
entity_parameters: z.string().optional(),
|
||||
});
|
||||
|
||||
const IMAGE_MODES = ['screensaver', 'camera', 'url'] as const;
|
||||
const imageConfigDefault = {
|
||||
mode: 'url' as const,
|
||||
...imageBaseConfigDefault,
|
||||
};
|
||||
|
||||
const imageConfigSchema = imageBaseConfigSchema
|
||||
.extend({
|
||||
mode: z.enum(IMAGE_MODES).default(imageConfigDefault.mode),
|
||||
})
|
||||
.merge(actionsSchema)
|
||||
.default(imageConfigDefault);
|
||||
export type ImageViewConfig = z.infer<typeof imageConfigSchema>;
|
||||
@@ -1127,8 +1127,6 @@ const go2rtcConfigSchema = z.object({
|
||||
stream: z.string().optional(),
|
||||
});
|
||||
|
||||
const liveImageConfigSchema = imageBaseConfigSchema;
|
||||
|
||||
const webrtcCardConfigSchema = z
|
||||
.object({
|
||||
entity: z.string().optional(),
|
||||
@@ -1302,9 +1300,6 @@ const cameraConfigDefault = {
|
||||
frigate: {
|
||||
client_id: 'frigate' as const,
|
||||
},
|
||||
image: {
|
||||
refresh_seconds: 1,
|
||||
},
|
||||
live_provider: 'auto' as const,
|
||||
motioneye: {
|
||||
images: {
|
||||
@@ -1408,7 +1403,7 @@ export const cameraConfigSchema = z
|
||||
// Live provider options.
|
||||
live_provider: z.enum(LIVE_PROVIDERS).default(cameraConfigDefault.live_provider),
|
||||
go2rtc: go2rtcConfigSchema.optional(),
|
||||
image: liveImageConfigSchema.default(cameraConfigDefault.image),
|
||||
image: imageBaseConfigSchema.optional().default(imageConfigDefault),
|
||||
jsmpeg: jsmpegConfigSchema.optional(),
|
||||
webrtc_card: webrtcCardConfigSchema.optional(),
|
||||
|
||||
|
||||
@@ -31,6 +31,10 @@ export const CONF_CAMERAS_ARRAY_GO2RTC_STREAM =
|
||||
`${CONF_CAMERAS}.#.go2rtc.stream` as const;
|
||||
export const CONF_CAMERAS_ARRAY_ICON = `${CONF_CAMERAS}.#.icon` as const;
|
||||
export const CONF_CAMERAS_ARRAY_ID = `${CONF_CAMERAS}.#.id` as const;
|
||||
export const CONF_CAMERAS_ARRAY_IMAGE_ENTITY = `${CONF_CAMERAS}.#.image.entity` as const;
|
||||
export const CONF_CAMERAS_ARRAY_IMAGE_ENTITY_PARAMETERS =
|
||||
`${CONF_CAMERAS}.#.image.entity_parameters` as const;
|
||||
export const CONF_CAMERAS_ARRAY_IMAGE_MODE = `${CONF_CAMERAS}.#.image.mode` as const;
|
||||
export const CONF_CAMERAS_ARRAY_IMAGE_REFRESH_SECONDS =
|
||||
`${CONF_CAMERAS}.#.image.refresh_seconds` as const;
|
||||
export const CONF_CAMERAS_ARRAY_IMAGE_URL = `${CONF_CAMERAS}.#.image.url` as const;
|
||||
@@ -290,6 +294,8 @@ export const CONF_LIVE_MICROPHONE_ALWAYS_CONNECTED =
|
||||
export const CONF_LIVE_ZOOMABLE = `${CONF_LIVE}.zoomable` as const;
|
||||
|
||||
const CONF_IMAGE = 'image' as const;
|
||||
export const CONF_IMAGE_ENTITY = `${CONF_IMAGE}.entity` as const;
|
||||
export const CONF_IMAGE_ENTITY_PARAMETERS = `${CONF_IMAGE}.entity_parameters` 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_URL = `${CONF_IMAGE}.url` as const;
|
||||
|
||||
+59
-17
@@ -66,6 +66,9 @@ import {
|
||||
CONF_CAMERAS_ARRAY_GO2RTC_STREAM,
|
||||
CONF_CAMERAS_ARRAY_ICON,
|
||||
CONF_CAMERAS_ARRAY_ID,
|
||||
CONF_CAMERAS_ARRAY_IMAGE_ENTITY,
|
||||
CONF_CAMERAS_ARRAY_IMAGE_ENTITY_PARAMETERS,
|
||||
CONF_CAMERAS_ARRAY_IMAGE_MODE,
|
||||
CONF_CAMERAS_ARRAY_IMAGE_REFRESH_SECONDS,
|
||||
CONF_CAMERAS_ARRAY_IMAGE_URL,
|
||||
CONF_CAMERAS_ARRAY_LIVE_PROVIDER,
|
||||
@@ -85,6 +88,8 @@ import {
|
||||
CONF_DIMENSIONS_ASPECT_RATIO_MODE,
|
||||
CONF_DIMENSIONS_MAX_HEIGHT,
|
||||
CONF_DIMENSIONS_MIN_HEIGHT,
|
||||
CONF_IMAGE_ENTITY,
|
||||
CONF_IMAGE_ENTITY_PARAMETERS,
|
||||
CONF_IMAGE_MODE,
|
||||
CONF_IMAGE_REFRESH_SECONDS,
|
||||
CONF_IMAGE_URL,
|
||||
@@ -519,9 +524,10 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
||||
|
||||
protected _imageModes: EditorSelectOption[] = [
|
||||
{ value: '', label: '' },
|
||||
{ value: 'camera', label: localize('config.image.modes.camera') },
|
||||
{ value: 'screensaver', label: localize('config.image.modes.screensaver') },
|
||||
{ value: 'url', label: localize('config.image.modes.url') },
|
||||
{ value: 'camera', label: localize('config.common.image.modes.camera') },
|
||||
{ value: 'entity', label: localize('config.common.image.modes.entity') },
|
||||
{ value: 'screensaver', label: localize('config.common.image.modes.screensaver') },
|
||||
{ value: 'url', label: localize('config.common.image.modes.url') },
|
||||
];
|
||||
|
||||
protected _timelineEventsMediaTypes: EditorSelectOption[] = [
|
||||
@@ -1733,6 +1739,36 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
||||
);
|
||||
}
|
||||
|
||||
protected _renderImageOptions(
|
||||
configPathMode: string,
|
||||
configPathUrl: string,
|
||||
configPathEntity: string,
|
||||
configPathEntityParameters: string,
|
||||
configPathRefreshSeconds: string,
|
||||
): TemplateResult {
|
||||
return html`
|
||||
${this._renderOptionSelector(configPathMode, this._imageModes, {
|
||||
label: localize('config.common.image.mode'),
|
||||
})}
|
||||
${this._renderStringInput(configPathUrl, {
|
||||
label: localize('config.common.image.url'),
|
||||
})}
|
||||
${this._renderOptionSelector(
|
||||
configPathEntity,
|
||||
this.hass ? getEntitiesFromHASS(this.hass) : [],
|
||||
{
|
||||
label: localize('config.common.image.entity'),
|
||||
},
|
||||
)}
|
||||
${this._renderStringInput(configPathEntityParameters, {
|
||||
label: localize('config.common.image.entity_parameters'),
|
||||
})}
|
||||
${this._renderNumberInput(configPathRefreshSeconds, {
|
||||
label: localize('config.common.image.refresh_seconds'),
|
||||
})}
|
||||
`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a camera section.
|
||||
* @param cameras The full array of cameras.
|
||||
@@ -2006,17 +2042,19 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
||||
true,
|
||||
'config.cameras.image.editor_label',
|
||||
{ name: 'mdi:image' },
|
||||
html`
|
||||
${this._renderNumberInput(
|
||||
getArrayConfigPath(
|
||||
CONF_CAMERAS_ARRAY_IMAGE_REFRESH_SECONDS,
|
||||
cameraIndex,
|
||||
),
|
||||
)}
|
||||
${this._renderStringInput(
|
||||
getArrayConfigPath(CONF_CAMERAS_ARRAY_IMAGE_URL, cameraIndex),
|
||||
)}
|
||||
`,
|
||||
this._renderImageOptions(
|
||||
getArrayConfigPath(CONF_CAMERAS_ARRAY_IMAGE_MODE, cameraIndex),
|
||||
getArrayConfigPath(CONF_CAMERAS_ARRAY_IMAGE_URL, cameraIndex),
|
||||
getArrayConfigPath(CONF_CAMERAS_ARRAY_IMAGE_ENTITY, cameraIndex),
|
||||
getArrayConfigPath(
|
||||
CONF_CAMERAS_ARRAY_IMAGE_ENTITY_PARAMETERS,
|
||||
cameraIndex,
|
||||
),
|
||||
getArrayConfigPath(
|
||||
CONF_CAMERAS_ARRAY_IMAGE_REFRESH_SECONDS,
|
||||
cameraIndex,
|
||||
),
|
||||
),
|
||||
)}
|
||||
${this._putInSubmenu(
|
||||
MENU_CAMERAS_WEBRTC_CARD,
|
||||
@@ -2725,9 +2763,13 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
||||
${this._renderOptionSetHeader('image')}
|
||||
${this._expandedMenus[MENU_OPTIONS] === 'image'
|
||||
? html` <div class="values">
|
||||
${this._renderOptionSelector(CONF_IMAGE_MODE, this._imageModes)}
|
||||
${this._renderStringInput(CONF_IMAGE_URL)}
|
||||
${this._renderNumberInput(CONF_IMAGE_REFRESH_SECONDS)}
|
||||
${this._renderImageOptions(
|
||||
CONF_IMAGE_MODE,
|
||||
CONF_IMAGE_URL,
|
||||
CONF_IMAGE_ENTITY,
|
||||
CONF_IMAGE_ENTITY_PARAMETERS,
|
||||
CONF_IMAGE_REFRESH_SECONDS,
|
||||
)}
|
||||
</div>`
|
||||
: ''}
|
||||
${this._renderOptionSetHeader('timeline')}
|
||||
|
||||
@@ -219,6 +219,19 @@
|
||||
"grid_selected_width_factor": "Augmenta l'amplada del suport multimèdia seleccionat en aquest factor",
|
||||
"mode": "Mode"
|
||||
},
|
||||
"image": {
|
||||
"entity": "",
|
||||
"entity_parameters": "",
|
||||
"mode": "Mode de visualització d'imatges",
|
||||
"modes": {
|
||||
"camera": "Instantània de l'entitat de la càmera de Home Assistant",
|
||||
"entity": "",
|
||||
"screensaver": "Logotip de Frigate incrustat",
|
||||
"url": "Imatge arbitrària especificada per URL"
|
||||
},
|
||||
"refresh_seconds": "Nombre de segons després dels quals cal actualitzar (0=mai)",
|
||||
"url": "URL d'imatge estàtica per a la visualització d'imatges"
|
||||
},
|
||||
"media_action_conditions": {
|
||||
"all": "Totes les oportunitats",
|
||||
"hidden": "A l'amagat del navegador/pestanya",
|
||||
@@ -257,17 +270,6 @@
|
||||
"max_height": "Alçada màxima de la targeta en unitats CSS (p. ex., '100vh')",
|
||||
"min_height": "Alçada mínima de la targeta en unitats CSS (p. ex., '100 px')"
|
||||
},
|
||||
"image": {
|
||||
"mode": "Mode de visualització d'imatges",
|
||||
"modes": {
|
||||
"camera": "Instantània de l'entitat de la càmera de Home Assistant",
|
||||
"screensaver": "Logotip de Frigate incrustat",
|
||||
"url": "Imatge arbitrària especificada per URL"
|
||||
},
|
||||
"refresh_seconds": "Nombre de segons després dels quals cal actualitzar (0=mai)",
|
||||
"url": "URL d'imatge estàtica per a la visualització d'imatges",
|
||||
"zoomable": "La imatge es pot ampliar/escombrar"
|
||||
},
|
||||
"live": {
|
||||
"auto_mute": "Silencia automàticament les càmeres en directe",
|
||||
"auto_pause": "Posa en pausa automàticament les càmeres en directe",
|
||||
|
||||
@@ -219,6 +219,19 @@
|
||||
"grid_selected_width_factor": "Increase selected media width by this factor",
|
||||
"mode": "Mode"
|
||||
},
|
||||
"image": {
|
||||
"entity": "Entity for use with entity mode",
|
||||
"entity_parameters": "Query parameters added to the entity-based picture URLs (e.g. width=1920&height=1080)",
|
||||
"mode": "Image mode",
|
||||
"modes": {
|
||||
"camera": "Home Assistant camera snapshot of camera entity",
|
||||
"entity": "Entity with entity_picture attribute",
|
||||
"screensaver": "Embedded Frigate logo",
|
||||
"url": "Arbitrary image specified by URL"
|
||||
},
|
||||
"refresh_seconds": "Number of seconds after which to refresh (0=never)",
|
||||
"url": "Static image URL"
|
||||
},
|
||||
"media_action_conditions": {
|
||||
"all": "All opportunities",
|
||||
"hidden": "On browser/tab hiding",
|
||||
@@ -257,17 +270,6 @@
|
||||
"max_height": "Maximum card height in CSS units (e.g. '100vh')",
|
||||
"min_height": "Minimum card height in CSS units (e.g. '100px')"
|
||||
},
|
||||
"image": {
|
||||
"mode": "Image view mode",
|
||||
"modes": {
|
||||
"camera": "Home Assistant camera snapshot of camera entity",
|
||||
"screensaver": "Embedded Frigate logo",
|
||||
"url": "Arbitrary image specified by URL"
|
||||
},
|
||||
"refresh_seconds": "Number of seconds after which to refresh (0=never)",
|
||||
"url": "Static image URL for image view",
|
||||
"zoomable": "Image can be zoomed/panned"
|
||||
},
|
||||
"live": {
|
||||
"auto_mute": "Automatically mute live cameras",
|
||||
"auto_pause": "Automatically pause live cameras",
|
||||
|
||||
@@ -219,6 +219,19 @@
|
||||
"grid_selected_width_factor": "Augmenter la largeur du média sélectionnée par ce facteur",
|
||||
"mode": "Mode"
|
||||
},
|
||||
"image": {
|
||||
"entity": "",
|
||||
"entity_parameters": "",
|
||||
"mode": "Mode d'affichage des images",
|
||||
"modes": {
|
||||
"camera": "Instantané de la caméra Home Assistant de l'entité caméra",
|
||||
"entity": "",
|
||||
"screensaver": "Logo Frigate intégré",
|
||||
"url": "Image arbitraire spécifiée par URL"
|
||||
},
|
||||
"refresh_seconds": "Nombre de secondes après lesquelles actualiser (0=jamais)",
|
||||
"url": "URL d'image statique pour l'affichage de l'image"
|
||||
},
|
||||
"media_action_conditions": {
|
||||
"all": "Toutes les opportunités",
|
||||
"hidden": "Sur le navigateur/onglet masqué",
|
||||
@@ -257,17 +270,6 @@
|
||||
"max_height": "Hauteur maximale de la carte en unités CSS (par exemple '100vh')",
|
||||
"min_height": "Hauteur minimale de la carte en unités CSS (par exemple « 100 px »)"
|
||||
},
|
||||
"image": {
|
||||
"mode": "Mode d'affichage des images",
|
||||
"modes": {
|
||||
"camera": "Instantané de la caméra Home Assistant de l'entité caméra",
|
||||
"screensaver": "Logo Frigate intégré",
|
||||
"url": "Image arbitraire spécifiée par URL"
|
||||
},
|
||||
"refresh_seconds": "Nombre de secondes après lesquelles actualiser (0=jamais)",
|
||||
"url": "URL d'image statique pour l'affichage de l'image",
|
||||
"zoomable": "L'image peut être zoomée/panoramique"
|
||||
},
|
||||
"live": {
|
||||
"auto_mute": "Couper automatiquement le son des caméras en direct",
|
||||
"auto_pause": "Mettre automatiquement en pause les caméras en direct",
|
||||
|
||||
@@ -219,6 +219,19 @@
|
||||
"grid_selected_width_factor": "",
|
||||
"mode": ""
|
||||
},
|
||||
"image": {
|
||||
"entity": "",
|
||||
"entity_parameters": "",
|
||||
"mode": "Modalità Visualizza immagine",
|
||||
"modes": {
|
||||
"camera": "Istantanea della telecamera di Home Assistant dell'entità telecamera",
|
||||
"entity": "",
|
||||
"screensaver": "Logo Frigate incorporato",
|
||||
"url": "Immagine arbitraria specificata dall'URL"
|
||||
},
|
||||
"refresh_seconds": "Numero di secondi dopo i quali aggiornare (0 = mai)",
|
||||
"url": "URL di immagine statica per la vista dell'immagine"
|
||||
},
|
||||
"media_action_conditions": {
|
||||
"all": "Tutte le opportunità",
|
||||
"hidden": "Sul browser/nascondere le schede",
|
||||
@@ -257,17 +270,6 @@
|
||||
"max_height": "",
|
||||
"min_height": ""
|
||||
},
|
||||
"image": {
|
||||
"mode": "Modalità Visualizza immagine",
|
||||
"modes": {
|
||||
"camera": "Istantanea della telecamera di Home Assistant dell'entità telecamera",
|
||||
"screensaver": "Logo Frigate incorporato",
|
||||
"url": "Immagine arbitraria specificata dall'URL"
|
||||
},
|
||||
"refresh_seconds": "Numero di secondi dopo i quali aggiornare (0 = mai)",
|
||||
"url": "URL di immagine statica per la vista dell'immagine",
|
||||
"zoomable": ""
|
||||
},
|
||||
"live": {
|
||||
"auto_mute": "Muta automaticamente le telecamere in diretta",
|
||||
"auto_pause": "Metti in pausa automaticamente le telecamere in diretta",
|
||||
|
||||
@@ -219,6 +219,19 @@
|
||||
"grid_selected_width_factor": "",
|
||||
"mode": ""
|
||||
},
|
||||
"image": {
|
||||
"entity": "",
|
||||
"entity_parameters": "",
|
||||
"mode": "Modo de visualização de imagem",
|
||||
"modes": {
|
||||
"camera": "Instantâneo da câmera do Home Assistant, da entidade de câmera",
|
||||
"entity": "",
|
||||
"screensaver": "Logo Frigate embutido",
|
||||
"url": "Imagem arbitrária especificada por URL"
|
||||
},
|
||||
"refresh_seconds": "Número de segundos após o qual atualizar (0 = nunca)",
|
||||
"url": "Imagem arbitrária especificada por URL"
|
||||
},
|
||||
"media_action_conditions": {
|
||||
"all": "Todas as oportunidades",
|
||||
"hidden": "Ao ocultar o navegador/aba",
|
||||
@@ -257,17 +270,6 @@
|
||||
"max_height": "",
|
||||
"min_height": ""
|
||||
},
|
||||
"image": {
|
||||
"mode": "Modo de visualização de imagem",
|
||||
"modes": {
|
||||
"camera": "Instantâneo da câmera do Home Assistant, da entidade de câmera",
|
||||
"screensaver": "Logo Frigate embutido",
|
||||
"url": "Imagem arbitrária especificada por URL"
|
||||
},
|
||||
"refresh_seconds": "Número de segundos após o qual atualizar (0 = nunca)",
|
||||
"url": "Imagem arbitrária especificada por URL",
|
||||
"zoomable": ""
|
||||
},
|
||||
"live": {
|
||||
"auto_mute": "Silenciar câmeras ao vivo automaticamente",
|
||||
"auto_pause": "Pausar câmeras ao vivo automaticamente",
|
||||
|
||||
@@ -219,6 +219,19 @@
|
||||
"grid_selected_width_factor": "",
|
||||
"mode": ""
|
||||
},
|
||||
"image": {
|
||||
"entity": "",
|
||||
"entity_parameters": "",
|
||||
"mode": "Modo de visualização de imagem",
|
||||
"modes": {
|
||||
"camera": "Instantâneo da câmera do Home Assistant, da entidade de câmera",
|
||||
"entity": "",
|
||||
"screensaver": "Logo Frigate embutido",
|
||||
"url": "Imagem arbitrária especificada por URL"
|
||||
},
|
||||
"refresh_seconds": "Número de segundos após o qual atualizar (0 = nunca)",
|
||||
"url": "Imagem arbitrária especificada por URL"
|
||||
},
|
||||
"media_action_conditions": {
|
||||
"all": "Todas as oportunidades",
|
||||
"hidden": "Ao ocultar o navegador/aba",
|
||||
@@ -257,17 +270,6 @@
|
||||
"max_height": "",
|
||||
"min_height": ""
|
||||
},
|
||||
"image": {
|
||||
"mode": "Modo de visualização de imagem",
|
||||
"modes": {
|
||||
"camera": "Instantâneo da câmera do Home Assistant, da entidade de câmera",
|
||||
"screensaver": "Logo Frigate embutido",
|
||||
"url": "Imagem arbitrária especificada por URL"
|
||||
},
|
||||
"refresh_seconds": "Número de segundos após o qual atualizar (0 = nunca)",
|
||||
"url": "Imagem arbitrária especificada por URL",
|
||||
"zoomable": ""
|
||||
},
|
||||
"live": {
|
||||
"auto_mute": "Silenciar câmeras ao vivo automaticamente",
|
||||
"auto_pause": "Parar câmeras ao vivo automaticamente",
|
||||
|
||||
@@ -24,6 +24,7 @@ describe('config defaults', () => {
|
||||
client_id: 'frigate',
|
||||
},
|
||||
image: {
|
||||
mode: 'auto',
|
||||
refresh_seconds: 1,
|
||||
},
|
||||
live_provider: 'auto',
|
||||
@@ -58,7 +59,7 @@ describe('config defaults', () => {
|
||||
min_height: '100px',
|
||||
},
|
||||
image: {
|
||||
mode: 'url',
|
||||
mode: 'auto',
|
||||
refresh_seconds: 1,
|
||||
},
|
||||
live: {
|
||||
|
||||
Reference in New Issue
Block a user