Basic configuration upgrade support.
This commit is contained in:
@@ -19,6 +19,8 @@
|
|||||||
"@material/image-list": "^12.0.0",
|
"@material/image-list": "^12.0.0",
|
||||||
"custom-card-helpers": "^1.8.0",
|
"custom-card-helpers": "^1.8.0",
|
||||||
"dayjs": "^1.10.7",
|
"dayjs": "^1.10.7",
|
||||||
|
"dlv": "github:developit/dlv",
|
||||||
|
"dset": "^3.1.1",
|
||||||
"embla-carousel": "^5.0.1",
|
"embla-carousel": "^5.0.1",
|
||||||
"home-assistant-js-websocket": "^5.11.1",
|
"home-assistant-js-websocket": "^5.11.1",
|
||||||
"lit": "^2.0.2",
|
"lit": "^2.0.2",
|
||||||
|
|||||||
+11
-4
@@ -20,7 +20,7 @@ import {
|
|||||||
import screenfull from 'screenfull';
|
import screenfull from 'screenfull';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|
||||||
import { entitySchema, frigateCardConfigSchema, MenuInteraction } from './types.js';
|
import { entitySchema, frigateCardConfigSchema, MenuInteraction, RawFrigateCardConfig } from './types.js';
|
||||||
import type {
|
import type {
|
||||||
BrowseMediaQueryParameters,
|
BrowseMediaQueryParameters,
|
||||||
Entity,
|
Entity,
|
||||||
@@ -58,6 +58,7 @@ import './patches/ha-hls-player.js';
|
|||||||
import cardStyle from './scss/card.scss';
|
import cardStyle from './scss/card.scss';
|
||||||
import { ResolvedMediaCache } from './resolved-media.js';
|
import { ResolvedMediaCache } from './resolved-media.js';
|
||||||
import { BrowseMediaUtil } from './browse-media-util.js';
|
import { BrowseMediaUtil } from './browse-media-util.js';
|
||||||
|
import { isConfigUpgradeable } from './config-mgmt.js';
|
||||||
|
|
||||||
/** A note on media callbacks:
|
/** A note on media callbacks:
|
||||||
*
|
*
|
||||||
@@ -401,19 +402,25 @@ export class FrigateCard extends LitElement {
|
|||||||
* Set the card configuration.
|
* Set the card configuration.
|
||||||
* @param inputConfig The card configuration.
|
* @param inputConfig The card configuration.
|
||||||
*/
|
*/
|
||||||
public setConfig(inputConfig: FrigateCardConfig): void {
|
public setConfig(inputConfig: RawFrigateCardConfig): void {
|
||||||
if (!inputConfig) {
|
if (!inputConfig) {
|
||||||
throw new Error(localize('error.invalid_configuration:'));
|
throw new Error(localize('error.invalid_configuration'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const configUpgradeable = isConfigUpgradeable(inputConfig);
|
||||||
const parseResult = frigateCardConfigSchema.safeParse(inputConfig);
|
const parseResult = frigateCardConfigSchema.safeParse(inputConfig);
|
||||||
if (!parseResult.success) {
|
if (!parseResult.success) {
|
||||||
const hint = this._getParseErrorPaths(parseResult.error);
|
const hint = this._getParseErrorPaths(parseResult.error);
|
||||||
|
let upgradeMessage = '';
|
||||||
|
if (configUpgradeable && getLovelace().mode !== 'yaml') {
|
||||||
|
upgradeMessage = `${localize('editor.upgrade_available_in_editor')}. `;
|
||||||
|
}
|
||||||
throw new Error(
|
throw new Error(
|
||||||
|
upgradeMessage +
|
||||||
`${localize('error.invalid_configuration')}: ` +
|
`${localize('error.invalid_configuration')}: ` +
|
||||||
(hint.length
|
(hint.length
|
||||||
? JSON.stringify(hint, null, ' ')
|
? JSON.stringify(hint, null, ' ')
|
||||||
: localize('error.invalid_configuration_no_hint')),
|
: localize('error.invalid_configuration_no_hint'))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
const config = parseResult.data;
|
const config = parseResult.data;
|
||||||
|
|||||||
@@ -0,0 +1,152 @@
|
|||||||
|
import delve from 'dlv';
|
||||||
|
import { dset } from 'dset';
|
||||||
|
import { RawFrigateCardConfig } from './types';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a configuration value.
|
||||||
|
* @param obj The configuration.
|
||||||
|
* @param key The key to the property to set.
|
||||||
|
* @param value The value to set.
|
||||||
|
*/
|
||||||
|
export const setConfigValue = (
|
||||||
|
obj: RawFrigateCardConfig,
|
||||||
|
key: string,
|
||||||
|
value: unknown,
|
||||||
|
): void => {
|
||||||
|
dset(obj, key, value);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a configuration value.
|
||||||
|
* @param obj The configuration.
|
||||||
|
* @param key The key to the property to retrieve.
|
||||||
|
* @returns The property or undefined if not found.
|
||||||
|
*/
|
||||||
|
export const getConfigValue = (obj: RawFrigateCardConfig, key: string, def?: unknown): unknown => {
|
||||||
|
return delve(obj, key, def);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete a configuration value.
|
||||||
|
* @param obj The configuration.
|
||||||
|
* @param key The key to the property to delete.
|
||||||
|
*/
|
||||||
|
export const deleteConfigValue = (obj: RawFrigateCardConfig, key: string): void => {
|
||||||
|
let id = key;
|
||||||
|
let targetObj: unknown = obj;
|
||||||
|
if (key && key.split && key.includes('.')) {
|
||||||
|
const keys = key.split('.');
|
||||||
|
id = keys[keys.length - 1];
|
||||||
|
targetObj = getConfigValue(obj, keys.slice(0, -1).join('.'));
|
||||||
|
}
|
||||||
|
if (targetObj && typeof targetObj === 'object') {
|
||||||
|
delete targetObj[id];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Upgrade a configuration.
|
||||||
|
* @param obj The configuration to upgrade.
|
||||||
|
* @returns `true` if the configuration is modified.
|
||||||
|
*/
|
||||||
|
export const upgradeConfig = function (obj: RawFrigateCardConfig): boolean {
|
||||||
|
let upgraded = false;
|
||||||
|
for (let i = 0; i < UPGRADES.length; i++) {
|
||||||
|
upgraded = UPGRADES[i](obj) || upgraded;
|
||||||
|
}
|
||||||
|
trimConfig(obj);
|
||||||
|
return upgraded;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if a configuration is automatically upgradeable.
|
||||||
|
* @param obj The configuration. It is not modified.
|
||||||
|
* @returns `true` if the configuration is upgradeable.
|
||||||
|
*/
|
||||||
|
export const isConfigUpgradeable = function (obj: RawFrigateCardConfig): boolean {
|
||||||
|
const newObj = JSON.parse(JSON.stringify(obj));
|
||||||
|
return upgradeConfig(newObj);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove empty sections from a configuration.
|
||||||
|
* @param obj Configuration object.
|
||||||
|
*/
|
||||||
|
export const trimConfig = function (obj: RawFrigateCardConfig): void {
|
||||||
|
const keys = Object.keys(obj);
|
||||||
|
for (let i = 0; i < keys.length; i++) {
|
||||||
|
const key = keys[i];
|
||||||
|
if (typeof obj[key] === 'object' && obj[key] != null) {
|
||||||
|
trimConfig(obj[key] as RawFrigateCardConfig);
|
||||||
|
|
||||||
|
if (!Object.keys(obj[key] as RawFrigateCardConfig).length) {
|
||||||
|
delete obj[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy a configuration.
|
||||||
|
* @param obj Configuration to copy.
|
||||||
|
* @returns A new deeply-copied configuration.
|
||||||
|
*/
|
||||||
|
export const copyConfig = function (obj: RawFrigateCardConfig): RawFrigateCardConfig {
|
||||||
|
return JSON.parse(JSON.stringify(obj));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines if a property is not an object.
|
||||||
|
* @param value The property.
|
||||||
|
* @returns `true` is the value is not an object.
|
||||||
|
*/
|
||||||
|
const isNotObject = function (value: unknown) {
|
||||||
|
return typeof value !== 'object' ? value : undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Move a property from one location to another.
|
||||||
|
* @param oldPath The old property path.
|
||||||
|
* @param newPath The new property path.
|
||||||
|
* @param transform An optional transform for the value.
|
||||||
|
* @returns `true` if the configuration was modified.
|
||||||
|
*/
|
||||||
|
const upgradeMoveTo = function (
|
||||||
|
oldPath: string,
|
||||||
|
newPath: string,
|
||||||
|
transform?: (valueIn: unknown) => unknown,
|
||||||
|
): (obj: RawFrigateCardConfig) => boolean {
|
||||||
|
return function (obj: RawFrigateCardConfig): boolean {
|
||||||
|
let value = getConfigValue(obj, oldPath);
|
||||||
|
if (transform) {
|
||||||
|
value = transform(value);
|
||||||
|
}
|
||||||
|
if (typeof value !== 'undefined') {
|
||||||
|
deleteConfigValue(obj, oldPath);
|
||||||
|
setConfigValue(obj, newPath, value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const UPGRADES = [
|
||||||
|
// v1.2.1 -> v2.0.0
|
||||||
|
upgradeMoveTo('frigate_url', 'frigate.url'),
|
||||||
|
upgradeMoveTo('frigate_client_id', 'frigate.client_id'),
|
||||||
|
upgradeMoveTo('frigate_camera_name', 'frigate.camera_name'),
|
||||||
|
upgradeMoveTo('label', 'frigate.label'),
|
||||||
|
upgradeMoveTo('zone', 'frigate.zone'),
|
||||||
|
upgradeMoveTo('view_default', 'view.default'),
|
||||||
|
upgradeMoveTo('view_timeout', 'view.timeout'),
|
||||||
|
upgradeMoveTo('live_provider', 'live.provider'),
|
||||||
|
upgradeMoveTo('live_preload', 'live.preload'),
|
||||||
|
upgradeMoveTo('webrtc', 'live.webrtc'),
|
||||||
|
upgradeMoveTo('autoplay_clip', 'event_viewer.autoplay_clip'),
|
||||||
|
upgradeMoveTo('controls.nextprev', 'event_viewer.controls.next_previous.style'),
|
||||||
|
upgradeMoveTo('controls.nextprev_size', 'event_viewer.controls.next_previous.size'),
|
||||||
|
upgradeMoveTo('menu_mode', 'menu.mode'),
|
||||||
|
upgradeMoveTo('menu_buttons', 'menu.buttons'),
|
||||||
|
upgradeMoveTo('menu_button_size', 'menu.button_size'),
|
||||||
|
upgradeMoveTo('image', 'image.src', isNotObject),
|
||||||
|
];
|
||||||
+269
-135
@@ -4,15 +4,40 @@ import { customElement, property, state } from 'lit/decorators.js';
|
|||||||
|
|
||||||
import { HomeAssistant, LovelaceCardEditor, fireEvent } from 'custom-card-helpers';
|
import { HomeAssistant, LovelaceCardEditor, fireEvent } from 'custom-card-helpers';
|
||||||
import { localize } from './localize/localize.js';
|
import { localize } from './localize/localize.js';
|
||||||
import {
|
import { frigateCardConfigDefaults, RawFrigateCardConfig } from './types.js';
|
||||||
FrigateCardConfig,
|
|
||||||
frigateCardConfigDefaults,
|
|
||||||
frigateCardConfigSchema,
|
|
||||||
} from './types.js';
|
|
||||||
|
|
||||||
import frigate_card_editor_style from './scss/editor.scss';
|
import frigate_card_editor_style from './scss/editor.scss';
|
||||||
|
import {
|
||||||
|
copyConfig,
|
||||||
|
deleteConfigValue,
|
||||||
|
getConfigValue,
|
||||||
|
isConfigUpgradeable,
|
||||||
|
setConfigValue,
|
||||||
|
trimConfig,
|
||||||
|
upgradeConfig,
|
||||||
|
} from './config-mgmt.js';
|
||||||
|
|
||||||
const options = {
|
interface EditorOptionsSet {
|
||||||
|
icon: string;
|
||||||
|
name: string;
|
||||||
|
secondary: string;
|
||||||
|
show: boolean;
|
||||||
|
}
|
||||||
|
interface EditorOptions {
|
||||||
|
[setName: string]: EditorOptionsSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface EditorOptionTarget {
|
||||||
|
configValue: string;
|
||||||
|
checked?: boolean;
|
||||||
|
value?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface EditorOptionSetTarget {
|
||||||
|
optionSetName: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const options: EditorOptions = {
|
||||||
basic: {
|
basic: {
|
||||||
icon: 'cog',
|
icon: 'cog',
|
||||||
name: localize('editor.basic'),
|
name: localize('editor.basic'),
|
||||||
@@ -58,7 +83,7 @@ const options = {
|
|||||||
dimensions: {
|
dimensions: {
|
||||||
icon: 'aspect-ratio',
|
icon: 'aspect-ratio',
|
||||||
name: localize('editor.dimensions'),
|
name: localize('editor.dimensions'),
|
||||||
secondary: localize('editor.dimensions'),
|
secondary: localize('editor.dimensions_secondary'),
|
||||||
show: false,
|
show: false,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -66,16 +91,18 @@ const options = {
|
|||||||
@customElement('frigate-card-editor')
|
@customElement('frigate-card-editor')
|
||||||
export class FrigateCardEditor extends LitElement implements LovelaceCardEditor {
|
export class FrigateCardEditor extends LitElement implements LovelaceCardEditor {
|
||||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||||
@state() private _config?: FrigateCardConfig;
|
@state() protected _config?: RawFrigateCardConfig;
|
||||||
@state() private _toggle?: boolean;
|
@state() protected _helpers?: any;
|
||||||
@state() private _helpers?: any;
|
protected _initialized = false;
|
||||||
private _initialized = false;
|
protected _configUpgradeable = false;
|
||||||
|
|
||||||
public setConfig(config: FrigateCardConfig): void {
|
public setConfig(config: RawFrigateCardConfig): void {
|
||||||
// Note: This does not use Zod to parse the configuration, so it may be
|
// Note: This does not use Zod to parse the configuration, so it may be
|
||||||
// partially or completely invalid. It's more useful to have a partially
|
// partially or completely invalid. It's more useful to have a partially
|
||||||
// valid configuration here, to allow the user to fix the broken parts.
|
// valid configuration here, to allow the user to fix the broken parts. As
|
||||||
|
// such, RawFrigateCardConfig is used as the type.
|
||||||
this._config = config;
|
this._config = config;
|
||||||
|
this._configUpgradeable = isConfigUpgradeable(config);
|
||||||
this.loadCardHelpers();
|
this.loadCardHelpers();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,7 +129,7 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected render(): TemplateResult | void {
|
protected render(): TemplateResult | void {
|
||||||
if (!this.hass || !this._helpers) {
|
if (!this.hass || !this._helpers || !this._config) {
|
||||||
return html``;
|
return html``;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,11 +138,9 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
|||||||
this._helpers.importMoreInfoControl('climate');
|
this._helpers.importMoreInfoControl('climate');
|
||||||
|
|
||||||
const cameraEntities = this._getEntities('camera');
|
const cameraEntities = this._getEntities('camera');
|
||||||
|
const webrtcCameraEntity = String(
|
||||||
const webrtcCameraEntity =
|
getConfigValue(this._config, 'live.webrtc.entity', ''),
|
||||||
this._config?.live?.webrtc && (this._config?.live.webrtc as any).entity
|
);
|
||||||
? (this._config?.live.webrtc as any).entity
|
|
||||||
: '';
|
|
||||||
|
|
||||||
const viewModes = {
|
const viewModes = {
|
||||||
'': '',
|
'': '',
|
||||||
@@ -172,8 +197,35 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
|||||||
const defaults = frigateCardConfigDefaults;
|
const defaults = frigateCardConfigDefaults;
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
|
${this._configUpgradeable
|
||||||
|
? html` <div class="upgrade">
|
||||||
|
<span>${localize('editor.upgrade_available')}</span>
|
||||||
|
<span>
|
||||||
|
<mwc-button
|
||||||
|
raised
|
||||||
|
label="${localize('editor.upgrade')}"
|
||||||
|
@click=${() => {
|
||||||
|
if (this._config) {
|
||||||
|
const upgradedConfig = copyConfig(this._config);
|
||||||
|
upgradeConfig(upgradedConfig);
|
||||||
|
this._config = upgradedConfig;
|
||||||
|
|
||||||
|
fireEvent(this, 'config-changed', { config: this._config });
|
||||||
|
this.requestUpdate();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
</mwc-button>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<br />`
|
||||||
|
: html``}
|
||||||
<div class="card-config">
|
<div class="card-config">
|
||||||
<div class="option" @click=${this._toggleOption} .option=${'basic'}>
|
<div
|
||||||
|
class="option"
|
||||||
|
@click=${this._toggleOptionHandler}
|
||||||
|
.optionSetName=${'basic'}
|
||||||
|
>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<ha-icon .icon=${`mdi:${options.basic.icon}`}></ha-icon>
|
<ha-icon .icon=${`mdi:${options.basic.icon}`}></ha-icon>
|
||||||
<div class="title">${options.basic.name}</div>
|
<div class="title">${options.basic.name}</div>
|
||||||
@@ -185,13 +237,13 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
|||||||
<div class="values">
|
<div class="values">
|
||||||
<paper-dropdown-menu
|
<paper-dropdown-menu
|
||||||
label=${localize('config.camera_entity')}
|
label=${localize('config.camera_entity')}
|
||||||
@value-changed=${this._valueChanged}
|
@value-changed=${this._valueChangedHandler}
|
||||||
.configValue=${'camera_entity'}
|
.configValue=${'camera_entity'}
|
||||||
>
|
>
|
||||||
<paper-listbox
|
<paper-listbox
|
||||||
slot="dropdown-content"
|
slot="dropdown-content"
|
||||||
.selected=${cameraEntities.indexOf(
|
.selected=${cameraEntities.indexOf(
|
||||||
this._config?.camera_entity || '',
|
String(getConfigValue(this._config, 'camera_entity', '')),
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
${cameraEntities.map((entity) => {
|
${cameraEntities.map((entity) => {
|
||||||
@@ -202,7 +254,11 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
|||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
: ''}
|
: ''}
|
||||||
<div class="option" @click=${this._toggleOption} .option=${'frigate'}>
|
<div
|
||||||
|
class="option"
|
||||||
|
@click=${this._toggleOptionHandler}
|
||||||
|
.optionSetName=${'frigate'}
|
||||||
|
>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<ha-icon .icon=${`mdi:${options.frigate.icon}`}></ha-icon>
|
<ha-icon .icon=${`mdi:${options.frigate.icon}`}></ha-icon>
|
||||||
<div class="title">${options.frigate.name}</div>
|
<div class="title">${options.frigate.name}</div>
|
||||||
@@ -214,38 +270,38 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
|||||||
<div class="values">
|
<div class="values">
|
||||||
<paper-input
|
<paper-input
|
||||||
label=${localize('config.frigate.camera_name')}
|
label=${localize('config.frigate.camera_name')}
|
||||||
.value=${this._config?.frigate?.camera_name || ''}
|
.value=${getConfigValue(this._config, 'frigate.camera_name', '')}
|
||||||
.configValue=${'frigate.camera_name'}
|
.configValue=${'frigate.camera_name'}
|
||||||
@value-changed=${this._valueChanged}
|
@value-changed=${this._valueChangedHandler}
|
||||||
></paper-input>
|
></paper-input>
|
||||||
<paper-input
|
<paper-input
|
||||||
label=${localize('config.frigate.url')}
|
label=${localize('config.frigate.url')}
|
||||||
.value=${this._config?.frigate?.url || ''}
|
.value=${getConfigValue(this._config, 'frigate.url', '')}
|
||||||
.configValue=${'frigate.url'}
|
.configValue=${'frigate.url'}
|
||||||
@value-changed=${this._valueChanged}
|
@value-changed=${this._valueChangedHandler}
|
||||||
></paper-input>
|
></paper-input>
|
||||||
<paper-input
|
<paper-input
|
||||||
.label=${localize('config.frigate.label')}
|
.label=${localize('config.frigate.label')}
|
||||||
.value=${this._config?.frigate?.label || ''}
|
.value=${getConfigValue(this._config, 'frigate.label', '')}
|
||||||
.configValue=${'frigate.label'}
|
.configValue=${'frigate.label'}
|
||||||
@value-changed=${this._valueChanged}
|
@value-changed=${this._valueChangedHandler}
|
||||||
></paper-input>
|
></paper-input>
|
||||||
<paper-input
|
<paper-input
|
||||||
.label=${localize('config.frigate.zone')}
|
.label=${localize('config.frigate.zone')}
|
||||||
.value=${this._config?.frigate?.zone || ''}
|
.value=${getConfigValue(this._config, 'frigate.zone', '')}
|
||||||
.configValue=${'frigate.zone'}
|
.configValue=${'frigate.zone'}
|
||||||
@value-changed=${this._valueChanged}
|
@value-changed=${this._valueChangedHandler}
|
||||||
></paper-input>
|
></paper-input>
|
||||||
<paper-input
|
<paper-input
|
||||||
label=${localize('config.frigate.client_id')}
|
label=${localize('config.frigate.client_id')}
|
||||||
.value=${this._config?.frigate?.client_id || ''}
|
.value=${getConfigValue(this._config, 'frigate.client_id', '')}
|
||||||
.configValue=${'frigate.client_id'}
|
.configValue=${'frigate.client_id'}
|
||||||
@value-changed=${this._valueChanged}
|
@value-changed=${this._valueChangedHandler}
|
||||||
></paper-input>
|
></paper-input>
|
||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
: ''}
|
: ''}
|
||||||
<div class="option" @click=${this._toggleOption} .option=${'view'}>
|
<div class="option" @click=${this._toggleOptionHandler} .optionSetName=${'view'}>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<ha-icon .icon=${`mdi:${options.view.icon}`}></ha-icon>
|
<ha-icon .icon=${`mdi:${options.view.icon}`}></ha-icon>
|
||||||
<div class="title">${options.view.name}</div>
|
<div class="title">${options.view.name}</div>
|
||||||
@@ -257,13 +313,13 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
|||||||
<div class="values">
|
<div class="values">
|
||||||
<paper-dropdown-menu
|
<paper-dropdown-menu
|
||||||
label=${localize('config.view.default')}
|
label=${localize('config.view.default')}
|
||||||
@value-changed=${this._valueChanged}
|
@value-changed=${this._valueChangedHandler}
|
||||||
.configValue=${'view.default'}
|
.configValue=${'view.default'}
|
||||||
>
|
>
|
||||||
<paper-listbox
|
<paper-listbox
|
||||||
slot="dropdown-content"
|
slot="dropdown-content"
|
||||||
.selected=${Object.keys(viewModes).indexOf(
|
.selected=${Object.keys(viewModes).indexOf(
|
||||||
this._config?.view?.default || '',
|
String(getConfigValue(this._config, 'view.default', '')),
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
${Object.keys(viewModes).map((key) => {
|
${Object.keys(viewModes).map((key) => {
|
||||||
@@ -277,16 +333,14 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
|||||||
label=${localize('config.view.timeout')}
|
label=${localize('config.view.timeout')}
|
||||||
prevent-invalid-input
|
prevent-invalid-input
|
||||||
allowed-pattern="[0-9]"
|
allowed-pattern="[0-9]"
|
||||||
.value=${this._config?.view?.timeout
|
.value=${getConfigValue(this._config, 'view.timeout', '')}
|
||||||
? String(this._config?.view?.timeout)
|
|
||||||
: ''}
|
|
||||||
.configValue=${'view.timeout'}
|
.configValue=${'view.timeout'}
|
||||||
@value-changed=${this._valueChanged}
|
@value-changed=${this._valueChangedHandler}
|
||||||
></paper-input>
|
></paper-input>
|
||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
: ''}
|
: ''}
|
||||||
<div class="option" @click=${this._toggleOption} .option=${'menu'}>
|
<div class="option" @click=${this._toggleOptionHandler} .optionSetName=${'menu'}>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<ha-icon .icon=${`mdi:${options.menu.icon}`}></ha-icon>
|
<ha-icon .icon=${`mdi:${options.menu.icon}`}></ha-icon>
|
||||||
<div class="title">${options.menu.name}</div>
|
<div class="title">${options.menu.name}</div>
|
||||||
@@ -298,13 +352,13 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
|||||||
<div class="values">
|
<div class="values">
|
||||||
<paper-dropdown-menu
|
<paper-dropdown-menu
|
||||||
.label=${localize('config.menu.mode')}
|
.label=${localize('config.menu.mode')}
|
||||||
@value-changed=${this._valueChanged}
|
@value-changed=${this._valueChangedHandler}
|
||||||
.configValue=${'menu.mode'}
|
.configValue=${'menu.mode'}
|
||||||
>
|
>
|
||||||
<paper-listbox
|
<paper-listbox
|
||||||
slot="dropdown-content"
|
slot="dropdown-content"
|
||||||
.selected=${Object.keys(menuModes).indexOf(
|
.selected=${Object.keys(menuModes).indexOf(
|
||||||
this._config?.menu?.mode || '',
|
String(getConfigValue(this._config, 'menu.mode', '')),
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
${Object.keys(menuModes).map((key) => {
|
${Object.keys(menuModes).map((key) => {
|
||||||
@@ -316,9 +370,9 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
|||||||
</paper-dropdown-menu>
|
</paper-dropdown-menu>
|
||||||
<paper-input
|
<paper-input
|
||||||
label=${localize('config.menu.button_size')}
|
label=${localize('config.menu.button_size')}
|
||||||
.value=${this._config?.menu?.button_size || ''}
|
.value=${getConfigValue(this._config, 'menu.button_size', '')}
|
||||||
.configValue=${'menu.button_size'}
|
.configValue=${'menu.button_size'}
|
||||||
@value-changed=${this._valueChanged}
|
@value-changed=${this._valueChangedHandler}
|
||||||
></paper-input>
|
></paper-input>
|
||||||
<ha-formfield
|
<ha-formfield
|
||||||
.label=${localize('editor.show_button') +
|
.label=${localize('editor.show_button') +
|
||||||
@@ -326,10 +380,13 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
|||||||
localize('config.menu.buttons.frigate')}
|
localize('config.menu.buttons.frigate')}
|
||||||
>
|
>
|
||||||
<ha-switch
|
<ha-switch
|
||||||
.checked=${this._config?.menu?.buttons?.frigate ??
|
.checked="${getConfigValue(
|
||||||
defaults.menu.buttons.frigate}
|
this._config,
|
||||||
|
'menu.buttons.frigate',
|
||||||
|
defaults.menu.buttons.frigate,
|
||||||
|
)},"
|
||||||
.configValue=${'menu.buttons.frigate'}
|
.configValue=${'menu.buttons.frigate'}
|
||||||
@change=${this._valueChanged}
|
@change=${this._valueChangedHandler}
|
||||||
></ha-switch>
|
></ha-switch>
|
||||||
</ha-formfield>
|
</ha-formfield>
|
||||||
<ha-formfield
|
<ha-formfield
|
||||||
@@ -338,10 +395,13 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
|||||||
localize('config.view.views.live')}
|
localize('config.view.views.live')}
|
||||||
>
|
>
|
||||||
<ha-switch
|
<ha-switch
|
||||||
.checked=${this._config?.menu?.buttons?.live ??
|
.checked=${getConfigValue(
|
||||||
defaults.menu.buttons.live}
|
this._config,
|
||||||
|
'menu.buttons.live',
|
||||||
|
defaults.menu.buttons.live,
|
||||||
|
)}
|
||||||
.configValue=${'menu.buttons.live'}
|
.configValue=${'menu.buttons.live'}
|
||||||
@change=${this._valueChanged}
|
@change=${this._valueChangedHandler}
|
||||||
></ha-switch>
|
></ha-switch>
|
||||||
</ha-formfield>
|
</ha-formfield>
|
||||||
<ha-formfield
|
<ha-formfield
|
||||||
@@ -350,10 +410,13 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
|||||||
localize('config.view.views.clips')}
|
localize('config.view.views.clips')}
|
||||||
>
|
>
|
||||||
<ha-switch
|
<ha-switch
|
||||||
.checked=${this._config?.menu?.buttons?.clips ??
|
.checked=${getConfigValue(
|
||||||
defaults.menu.buttons.clips}
|
this._config,
|
||||||
|
'menu.buttons.clips',
|
||||||
|
defaults.menu.buttons.clips,
|
||||||
|
)}
|
||||||
.configValue=${'menu.buttons.clips'}
|
.configValue=${'menu.buttons.clips'}
|
||||||
@change=${this._valueChanged}
|
@change=${this._valueChangedHandler}
|
||||||
></ha-switch>
|
></ha-switch>
|
||||||
</ha-formfield>
|
</ha-formfield>
|
||||||
<ha-formfield
|
<ha-formfield
|
||||||
@@ -362,10 +425,13 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
|||||||
localize('config.view.views.snapshots')}
|
localize('config.view.views.snapshots')}
|
||||||
>
|
>
|
||||||
<ha-switch
|
<ha-switch
|
||||||
.checked=${this._config?.menu?.buttons?.snapshots ??
|
.checked=${getConfigValue(
|
||||||
defaults.menu.buttons.snapshots}
|
this._config,
|
||||||
|
'menu.buttons.snapshots',
|
||||||
|
defaults.menu.buttons.snapshots,
|
||||||
|
)}
|
||||||
.configValue=${'menu.buttons.snapshots'}
|
.configValue=${'menu.buttons.snapshots'}
|
||||||
@change=${this._valueChanged}
|
@change=${this._valueChangedHandler}
|
||||||
></ha-switch>
|
></ha-switch>
|
||||||
</ha-formfield>
|
</ha-formfield>
|
||||||
<ha-formfield
|
<ha-formfield
|
||||||
@@ -374,10 +440,13 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
|||||||
localize('config.view.views.image')}
|
localize('config.view.views.image')}
|
||||||
>
|
>
|
||||||
<ha-switch
|
<ha-switch
|
||||||
.checked=${this._config?.menu?.buttons?.image ??
|
.checked=${getConfigValue(
|
||||||
defaults.menu.buttons.image}
|
this._config,
|
||||||
|
'menu.buttons.image',
|
||||||
|
defaults.menu.buttons.image,
|
||||||
|
)}
|
||||||
.configValue=${'menu.buttons.image'}
|
.configValue=${'menu.buttons.image'}
|
||||||
@change=${this._valueChanged}
|
@change=${this._valueChangedHandler}
|
||||||
></ha-switch>
|
></ha-switch>
|
||||||
</ha-formfield>
|
</ha-formfield>
|
||||||
<ha-formfield
|
<ha-formfield
|
||||||
@@ -386,10 +455,13 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
|||||||
localize('config.menu.buttons.download')}
|
localize('config.menu.buttons.download')}
|
||||||
>
|
>
|
||||||
<ha-switch
|
<ha-switch
|
||||||
.checked=${this._config?.menu?.buttons?.download ??
|
.checked=${getConfigValue(
|
||||||
defaults.menu.buttons.download}
|
this._config,
|
||||||
|
'menu.buttons.download',
|
||||||
|
defaults.menu.buttons.download,
|
||||||
|
)}
|
||||||
.configValue=${'menu.buttons.download'}
|
.configValue=${'menu.buttons.download'}
|
||||||
@change=${this._valueChanged}
|
@change=${this._valueChangedHandler}
|
||||||
></ha-switch>
|
></ha-switch>
|
||||||
</ha-formfield>
|
</ha-formfield>
|
||||||
<ha-formfield
|
<ha-formfield
|
||||||
@@ -398,10 +470,13 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
|||||||
localize('config.menu.buttons.frigate_ui')}
|
localize('config.menu.buttons.frigate_ui')}
|
||||||
>
|
>
|
||||||
<ha-switch
|
<ha-switch
|
||||||
.checked=${this._config?.menu?.buttons?.frigate_ui ??
|
.checked=${getConfigValue(
|
||||||
defaults.menu.buttons.frigate_ui}
|
this._config,
|
||||||
|
'menu.buttons.frigate_ui',
|
||||||
|
defaults.menu.buttons.frigate_ui,
|
||||||
|
)}
|
||||||
.configValue=${'menu.buttons.frigate_ui'}
|
.configValue=${'menu.buttons.frigate_ui'}
|
||||||
@change=${this._valueChanged}
|
@change=${this._valueChangedHandler}
|
||||||
></ha-switch>
|
></ha-switch>
|
||||||
</ha-formfield>
|
</ha-formfield>
|
||||||
<ha-formfield
|
<ha-formfield
|
||||||
@@ -410,16 +485,19 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
|||||||
localize('config.menu.buttons.fullscreen')}
|
localize('config.menu.buttons.fullscreen')}
|
||||||
>
|
>
|
||||||
<ha-switch
|
<ha-switch
|
||||||
.checked=${this._config?.menu?.buttons?.fullscreen ??
|
.checked=${getConfigValue(
|
||||||
defaults.menu.buttons.fullscreen}
|
this._config,
|
||||||
|
'menu.buttons.fullscreen',
|
||||||
|
defaults.menu.buttons.fullscreen,
|
||||||
|
)}
|
||||||
.configValue=${'menu.buttons.fullscreen'}
|
.configValue=${'menu.buttons.fullscreen'}
|
||||||
@change=${this._valueChanged}
|
@change=${this._valueChangedHandler}
|
||||||
></ha-switch>
|
></ha-switch>
|
||||||
</ha-formfield>
|
</ha-formfield>
|
||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
: ''}
|
: ''}
|
||||||
<div class="option" @click=${this._toggleOption} .option=${'live'}>
|
<div class="option" @click=${this._toggleOptionHandler} .optionSetName=${'live'}>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<ha-icon .icon=${`mdi:${options.live.icon}`}></ha-icon>
|
<ha-icon .icon=${`mdi:${options.live.icon}`}></ha-icon>
|
||||||
<div class="title">${options.live.name}</div>
|
<div class="title">${options.live.name}</div>
|
||||||
@@ -432,20 +510,24 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
|||||||
<br />
|
<br />
|
||||||
<ha-formfield .label=${localize('config.live.preload')}>
|
<ha-formfield .label=${localize('config.live.preload')}>
|
||||||
<ha-switch
|
<ha-switch
|
||||||
.checked=${this._config?.live?.preload ?? defaults.live.preload}
|
.checked=${getConfigValue(
|
||||||
|
this._config,
|
||||||
|
'live.preload',
|
||||||
|
defaults.live.preload,
|
||||||
|
)}
|
||||||
.configValue=${'live.preload'}
|
.configValue=${'live.preload'}
|
||||||
@change=${this._valueChanged}
|
@change=${this._valueChangedHandler}
|
||||||
></ha-switch>
|
></ha-switch>
|
||||||
</ha-formfield>
|
</ha-formfield>
|
||||||
<paper-dropdown-menu
|
<paper-dropdown-menu
|
||||||
.label=${localize('config.live.provider')}
|
.label=${localize('config.live.provider')}
|
||||||
@value-changed=${this._valueChanged}
|
@value-changed=${this._valueChangedHandler}
|
||||||
.configValue=${'live.provider'}
|
.configValue=${'live.provider'}
|
||||||
>
|
>
|
||||||
<paper-listbox
|
<paper-listbox
|
||||||
slot="dropdown-content"
|
slot="dropdown-content"
|
||||||
.selected=${Object.keys(liveProviders).indexOf(
|
.selected=${Object.keys(liveProviders).indexOf(
|
||||||
this._config?.live?.provider || '',
|
String(getConfigValue(this._config, 'live.provider', '')),
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
${Object.keys(liveProviders).map((key) => {
|
${Object.keys(liveProviders).map((key) => {
|
||||||
@@ -457,7 +539,7 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
|||||||
</paper-dropdown-menu>
|
</paper-dropdown-menu>
|
||||||
<paper-dropdown-menu
|
<paper-dropdown-menu
|
||||||
.label=${localize('config.live.webrtc.entity')}
|
.label=${localize('config.live.webrtc.entity')}
|
||||||
@value-changed=${this._valueChanged}
|
@value-changed=${this._valueChangedHandler}
|
||||||
.configValue=${'live.webrtc.entity'}
|
.configValue=${'live.webrtc.entity'}
|
||||||
>
|
>
|
||||||
<paper-listbox
|
<paper-listbox
|
||||||
@@ -471,14 +553,18 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
|||||||
</paper-dropdown-menu>
|
</paper-dropdown-menu>
|
||||||
<paper-input
|
<paper-input
|
||||||
label=${localize('config.live.webrtc.url')}
|
label=${localize('config.live.webrtc.url')}
|
||||||
.value=${this._config?.live?.webrtc?.url || ''}
|
.value=${getConfigValue(this._config, 'live.webrtc.url', '')}
|
||||||
.configValue=${'live.webrtc.url'}
|
.configValue=${'live.webrtc.url'}
|
||||||
@value-changed=${this._valueChanged}
|
@value-changed=${this._valueChangedHandler}
|
||||||
></paper-input>
|
></paper-input>
|
||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
: ''}
|
: ''}
|
||||||
<div class="option" @click=${this._toggleOption} .option=${'event_viewer'}>
|
<div
|
||||||
|
class="option"
|
||||||
|
@click=${this._toggleOptionHandler}
|
||||||
|
.optionSetName=${'event_viewer'}
|
||||||
|
>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<ha-icon .icon=${`mdi:${options.event_viewer.icon}`}></ha-icon>
|
<ha-icon .icon=${`mdi:${options.event_viewer.icon}`}></ha-icon>
|
||||||
<div class="title">${options.event_viewer.name}</div>
|
<div class="title">${options.event_viewer.name}</div>
|
||||||
@@ -490,29 +576,41 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
|||||||
<br />
|
<br />
|
||||||
<ha-formfield .label=${localize('config.event_viewer.autoplay_clip')}>
|
<ha-formfield .label=${localize('config.event_viewer.autoplay_clip')}>
|
||||||
<ha-switch
|
<ha-switch
|
||||||
.checked=${this._config?.event_viewer?.autoplay_clip ??
|
.checked=${getConfigValue(
|
||||||
defaults.event_viewer.autoplay_clip}
|
this._config,
|
||||||
|
'event_viewer.autoplay_clip',
|
||||||
|
defaults.event_viewer.autoplay_clip,
|
||||||
|
)}
|
||||||
.configValue=${'event_viewer.autoplay_clip'}
|
.configValue=${'event_viewer.autoplay_clip'}
|
||||||
@change=${this._valueChanged}
|
@change=${this._valueChangedHandler}
|
||||||
></ha-switch>
|
></ha-switch>
|
||||||
</ha-formfield>
|
</ha-formfield>
|
||||||
<ha-formfield .label=${localize('config.event_viewer.lazy_load')}>
|
<ha-formfield .label=${localize('config.event_viewer.lazy_load')}>
|
||||||
<ha-switch
|
<ha-switch
|
||||||
.checked=${this._config?.event_viewer?.lazy_load ??
|
.checked=${getConfigValue(
|
||||||
defaults.event_viewer.lazy_load}
|
this._config,
|
||||||
|
'event_viewer.lazy_load',
|
||||||
|
defaults.event_viewer.lazy_load,
|
||||||
|
)}
|
||||||
.configValue=${'event_viewer.lazy_load'}
|
.configValue=${'event_viewer.lazy_load'}
|
||||||
@change=${this._valueChanged}
|
@change=${this._valueChangedHandler}
|
||||||
></ha-switch>
|
></ha-switch>
|
||||||
</ha-formfield>
|
</ha-formfield>
|
||||||
<paper-dropdown-menu
|
<paper-dropdown-menu
|
||||||
.label=${localize('config.event_viewer.controls.next_previous.style')}
|
.label=${localize('config.event_viewer.controls.next_previous.style')}
|
||||||
@value-changed=${this._valueChanged}
|
@value-changed=${this._valueChangedHandler}
|
||||||
.configValue=${'event_viewer.controls.next_previous.style'}
|
.configValue=${'event_viewer.controls.next_previous.style'}
|
||||||
>
|
>
|
||||||
<paper-listbox
|
<paper-listbox
|
||||||
slot="dropdown-content"
|
slot="dropdown-content"
|
||||||
.selected=${Object.keys(eventViewerNextPreviousControlStyles).indexOf(
|
.selected=${Object.keys(eventViewerNextPreviousControlStyles).indexOf(
|
||||||
this._config?.event_viewer?.controls?.next_previous?.style || '',
|
String(
|
||||||
|
getConfigValue(
|
||||||
|
this._config,
|
||||||
|
'event_viewer.controls.next_previous.style',
|
||||||
|
'',
|
||||||
|
),
|
||||||
|
),
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
${Object.keys(eventViewerNextPreviousControlStyles).map((key) => {
|
${Object.keys(eventViewerNextPreviousControlStyles).map((key) => {
|
||||||
@@ -526,13 +624,21 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
|||||||
</paper-dropdown-menu>
|
</paper-dropdown-menu>
|
||||||
<paper-input
|
<paper-input
|
||||||
label=${localize('config.event_viewer.controls.next_previous.size')}
|
label=${localize('config.event_viewer.controls.next_previous.size')}
|
||||||
.value=${this._config?.event_viewer?.controls?.next_previous?.size || ''}
|
.value=${getConfigValue(
|
||||||
|
this._config,
|
||||||
|
'event_viewer.controls.next_previous.size',
|
||||||
|
'',
|
||||||
|
)}
|
||||||
.configValue=${'event_viewer.controls.next_previous.size'}
|
.configValue=${'event_viewer.controls.next_previous.size'}
|
||||||
@value-changed=${this._valueChanged}
|
@value-changed=${this._valueChangedHandler}
|
||||||
></paper-input>
|
></paper-input>
|
||||||
</div>`
|
</div>`
|
||||||
: ''}
|
: ''}
|
||||||
<div class="option" @click=${this._toggleOption} .option=${'image'}>
|
<div
|
||||||
|
class="option"
|
||||||
|
@click=${this._toggleOptionHandler}
|
||||||
|
.optionSetName=${'image'}
|
||||||
|
>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<ha-icon .icon=${`mdi:${options.image.icon}`}></ha-icon>
|
<ha-icon .icon=${`mdi:${options.image.icon}`}></ha-icon>
|
||||||
<div class="title">${options.image.name}</div>
|
<div class="title">${options.image.name}</div>
|
||||||
@@ -544,15 +650,17 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
|||||||
<paper-input
|
<paper-input
|
||||||
label=${localize('config.image.src')}
|
label=${localize('config.image.src')}
|
||||||
prevent-invalid-input
|
prevent-invalid-input
|
||||||
.value=${this._config?.image?.src
|
.value=${getConfigValue(this._config, 'image.src', '')}
|
||||||
? String(this._config?.image?.src)
|
|
||||||
: ''}
|
|
||||||
.configValue=${'image.src'}
|
.configValue=${'image.src'}
|
||||||
@value-changed=${this._valueChanged}
|
@value-changed=${this._valueChangedHandler}
|
||||||
></paper-input>
|
></paper-input>
|
||||||
</div>`
|
</div>`
|
||||||
: ''}
|
: ''}
|
||||||
<div class="option" @click=${this._toggleOption} .option=${'dimensions'}>
|
<div
|
||||||
|
class="option"
|
||||||
|
@click=${this._toggleOptionHandler}
|
||||||
|
.optionSetName=${'dimensions'}
|
||||||
|
>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<ha-icon .icon=${`mdi:${options.dimensions.icon}`}></ha-icon>
|
<ha-icon .icon=${`mdi:${options.dimensions.icon}`}></ha-icon>
|
||||||
<div class="title">${options.dimensions.name}</div>
|
<div class="title">${options.dimensions.name}</div>
|
||||||
@@ -563,13 +671,15 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
|||||||
? html` <div class="values">
|
? html` <div class="values">
|
||||||
<paper-dropdown-menu
|
<paper-dropdown-menu
|
||||||
.label=${localize('config.dimensions.aspect_ratio_mode')}
|
.label=${localize('config.dimensions.aspect_ratio_mode')}
|
||||||
@value-changed=${this._valueChanged}
|
@value-changed=${this._valueChangedHandler}
|
||||||
.configValue=${'dimensions.aspect_ratio_mode'}
|
.configValue=${'dimensions.aspect_ratio_mode'}
|
||||||
>
|
>
|
||||||
<paper-listbox
|
<paper-listbox
|
||||||
slot="dropdown-content"
|
slot="dropdown-content"
|
||||||
.selected=${Object.keys(aspectRatioModes).indexOf(
|
.selected=${Object.keys(aspectRatioModes).indexOf(
|
||||||
this._config?.dimensions?.aspect_ratio_mode || '',
|
String(
|
||||||
|
getConfigValue(this._config, 'dimensions.aspect_ratio_mode', ''),
|
||||||
|
),
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
${Object.keys(aspectRatioModes).map((key) => {
|
${Object.keys(aspectRatioModes).map((key) => {
|
||||||
@@ -582,11 +692,9 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
|||||||
<paper-input
|
<paper-input
|
||||||
label=${localize('config.dimensions.aspect_ratio')}
|
label=${localize('config.dimensions.aspect_ratio')}
|
||||||
prevent-invalid-input
|
prevent-invalid-input
|
||||||
.value=${this._config?.dimensions?.aspect_ratio
|
.value=${getConfigValue(this._config, 'dimensions.aspect_ratio', '')}
|
||||||
? String(this._config?.dimensions?.aspect_ratio)
|
|
||||||
: ''}
|
|
||||||
.configValue=${'dimensions.aspect_ratio'}
|
.configValue=${'dimensions.aspect_ratio'}
|
||||||
@value-changed=${this._valueChanged}
|
@value-changed=${this._valueChangedHandler}
|
||||||
></paper-input>
|
></paper-input>
|
||||||
</div>`
|
</div>`
|
||||||
: ''}
|
: ''}
|
||||||
@@ -594,69 +702,95 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _initialize(): void {
|
/**
|
||||||
|
* Verify editor is initialized.
|
||||||
|
*/
|
||||||
|
protected _initialize(): void {
|
||||||
if (this.hass === undefined) return;
|
if (this.hass === undefined) return;
|
||||||
if (this._config === undefined) return;
|
if (this._config === undefined) return;
|
||||||
if (this._helpers === undefined) return;
|
if (this._helpers === undefined) return;
|
||||||
this._initialized = true;
|
this._initialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async loadCardHelpers(): Promise<void> {
|
/**
|
||||||
|
* Load card helpers.
|
||||||
|
*/
|
||||||
|
protected async loadCardHelpers(): Promise<void> {
|
||||||
this._helpers = await (window as any).loadCardHelpers();
|
this._helpers = await (window as any).loadCardHelpers();
|
||||||
}
|
}
|
||||||
|
|
||||||
private _toggleOption(ev): void {
|
/**
|
||||||
this._toggleThing(ev, options);
|
* Handle a toggled set of options.
|
||||||
|
* @param ev The event triggering the change.
|
||||||
|
*/
|
||||||
|
protected _toggleOptionHandler(ev: { target: EditorOptionSetTarget | null }): void {
|
||||||
|
this._toggleOptionSet(ev, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _toggleThing(ev, optionList): void {
|
/**
|
||||||
const show = !optionList[ev.target.option].show;
|
* Toggle display of a set of options (e.g. 'Live')
|
||||||
for (const [key] of Object.entries(optionList)) {
|
* @param ev The event triggering the change.
|
||||||
optionList[key].show = false;
|
* @param options The EditorOptions object.
|
||||||
|
*/
|
||||||
|
protected _toggleOptionSet(
|
||||||
|
ev: { target: EditorOptionSetTarget | null },
|
||||||
|
options: EditorOptions,
|
||||||
|
): void {
|
||||||
|
if (ev && ev.target) {
|
||||||
|
const show = !options[ev.target.optionSetName].show;
|
||||||
|
for (const [key] of Object.entries(options)) {
|
||||||
|
options[key].show = false;
|
||||||
|
}
|
||||||
|
options[ev.target.optionSetName].show = show;
|
||||||
|
this.requestUpdate();
|
||||||
}
|
}
|
||||||
optionList[ev.target.option].show = show;
|
|
||||||
this._toggle = !this._toggle;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private _valueChanged(ev): void {
|
/**
|
||||||
if (!this._config || !this.hass) {
|
* Handle a changed option value.
|
||||||
|
* @param ev Event triggering the change.
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
protected _valueChangedHandler(ev: {
|
||||||
|
target: (EditorOptionTarget & HTMLElement) | null;
|
||||||
|
}): void {
|
||||||
|
const target = ev.target;
|
||||||
|
if (!this._config || !this.hass || !target) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const target = ev.target;
|
|
||||||
const value = target.value?.trim();
|
|
||||||
let key: string = target.configValue;
|
|
||||||
|
|
||||||
|
let value;
|
||||||
|
if ('checked' in target) {
|
||||||
|
value = target.checked;
|
||||||
|
} else {
|
||||||
|
value = target.value?.trim();
|
||||||
|
}
|
||||||
|
const key: string = target.configValue;
|
||||||
if (!key) {
|
if (!key) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Need to deep copy the config so cannot use Object.assign.
|
const newConfig = copyConfig(this._config);
|
||||||
const newConfig = JSON.parse(JSON.stringify(this._config));
|
if (value === '' || typeof value === 'undefined') {
|
||||||
let objectTarget = newConfig;
|
// Don't delete empty properties that are from a dropdown menu. An empty
|
||||||
|
// property in that context may just be a user-entered value that is not
|
||||||
if (key.includes('.')) {
|
// in the valid choices in the dropdown. This probably won't end well for
|
||||||
const parts = key.split('.');
|
// the user anyway, but having the whole property deleted the moment they
|
||||||
objectTarget = parts.slice(0, -1).reduce((obj, key) => {
|
// press a key is very jarring.
|
||||||
if (!(key in obj)) {
|
if (target.tagName != 'PAPER-DROPDOWN-MENU') {
|
||||||
obj[key] = {};
|
deleteConfigValue(newConfig, key);
|
||||||
}
|
}
|
||||||
return obj[key];
|
|
||||||
}, newConfig);
|
|
||||||
key = parts[parts.length - 1];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value !== undefined && objectTarget[key] === value) {
|
|
||||||
return;
|
|
||||||
} else if (value === '') {
|
|
||||||
delete objectTarget[key];
|
|
||||||
} else {
|
} else {
|
||||||
objectTarget[key] = target.checked !== undefined ? target.checked : value;
|
setConfigValue(newConfig, key, value);
|
||||||
}
|
}
|
||||||
|
trimConfig(newConfig);
|
||||||
this._config = newConfig;
|
this._config = newConfig;
|
||||||
fireEvent(this, 'config-changed', { config: this._config });
|
fireEvent(this, 'config-changed', { config: this._config });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return compiled CSS styles (thus safe to use with unsafeCSS).
|
/**
|
||||||
|
* Return compiled CSS styles.
|
||||||
|
*/
|
||||||
static get styles(): CSSResultGroup {
|
static get styles(): CSSResultGroup {
|
||||||
return unsafeCSS(frigate_card_editor_style);
|
return unsafeCSS(frigate_card_editor_style);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -101,22 +101,25 @@
|
|||||||
},
|
},
|
||||||
"editor": {
|
"editor": {
|
||||||
"basic": "Basic",
|
"basic": "Basic",
|
||||||
"basic_secondary": "Basic options for most users",
|
"basic_secondary": "Options for most users",
|
||||||
"frigate": "Frigate",
|
"frigate": "Frigate",
|
||||||
"frigate_secondary": "Frigate server options",
|
"frigate_secondary": "Frigate server options",
|
||||||
"view": "View",
|
"view": "View",
|
||||||
"view_secondary": "Card view options",
|
"view_secondary": "What the card should show and how to show it",
|
||||||
"menu": "Menu",
|
"menu": "Menu",
|
||||||
"menu_secondary": "Menu options",
|
"menu_secondary": "Menu look & feel options",
|
||||||
"live": "Live",
|
"live": "Live",
|
||||||
"live_secondary": "Live view options",
|
"live_secondary": "Live camera view options",
|
||||||
"event_viewer": "Event viewer",
|
"event_viewer": "Event viewer",
|
||||||
"event_viewer_secondary": "Event viewer options",
|
"event_viewer_secondary": "Snapshots & clips gallery options",
|
||||||
"image": "Image",
|
"image": "Image",
|
||||||
"image_secondary": "Static image view options",
|
"image_secondary": "Static image view options",
|
||||||
"dimensions": "Dimensions",
|
"dimensions": "Dimensions",
|
||||||
"dimensions_secondary": "Card dimensions options",
|
"dimensions_secondary": "Dimensions & shape options",
|
||||||
"show_button": "Show button"
|
"show_button": "Show button",
|
||||||
|
"upgrade": "Upgrade",
|
||||||
|
"upgrade_available": "An automatic card configuration upgrade is available",
|
||||||
|
"upgrade_available_in_editor": "An automatic card configuration upgrade is available in the editor"
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
"empty_response": "Received empty response from Home Assistant for request",
|
"empty_response": "Received empty response from Home Assistant for request",
|
||||||
|
|||||||
@@ -25,3 +25,15 @@
|
|||||||
ha-formfield {
|
ha-formfield {
|
||||||
padding-bottom: 8px;
|
padding-bottom: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
div.upgrade {
|
||||||
|
width: auto;
|
||||||
|
border: 1px dotted var(--primary-color);
|
||||||
|
margin: 10px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
div.upgrade span {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
@@ -1,3 +1,7 @@
|
|||||||
|
:host {
|
||||||
|
--video-max-height: none;
|
||||||
|
}
|
||||||
|
|
||||||
canvas {
|
canvas {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
display: block;
|
display: block;
|
||||||
|
|||||||
@@ -466,6 +466,7 @@ export const frigateCardConfigSchema = z.object({
|
|||||||
test_gui: z.boolean().optional(),
|
test_gui: z.boolean().optional(),
|
||||||
});
|
});
|
||||||
export type FrigateCardConfig = z.infer<typeof frigateCardConfigSchema>;
|
export type FrigateCardConfig = z.infer<typeof frigateCardConfigSchema>;
|
||||||
|
export type RawFrigateCardConfig = Record<string, unknown>;
|
||||||
|
|
||||||
export const frigateCardConfigDefaults = {
|
export const frigateCardConfigDefaults = {
|
||||||
frigate: frigateConfigDefault,
|
frigate: frigateConfigDefault,
|
||||||
|
|||||||
Reference in New Issue
Block a user