Add support for arbitrary image entities
This commit is contained in:
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user