Merge pull request #1518 from dermotduffy/entity-picture

Add support for arbitrary image entities
This commit is contained in:
Dermot Duffy
2024-09-08 17:04:05 -07:00
committed by GitHub
16 changed files with 264 additions and 149 deletions
+4
View File
@@ -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:
+7 -4
View File
@@ -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`
+13 -8
View File
@@ -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
View File
@@ -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'), {
+1 -8
View File
@@ -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
View File
@@ -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(),
+6
View File
@@ -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
View File
@@ -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')}
+13 -11
View File
@@ -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",
+13 -11
View File
@@ -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",
+13 -11
View File
@@ -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",
+13 -11
View File
@@ -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",
+13 -11
View File
@@ -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",
+13 -11
View File
@@ -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",
+2 -1
View File
@@ -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: {