Support hiding menu buttons. Add editor localization.

This commit is contained in:
Dermot Duffy
2021-09-03 22:43:06 -07:00
parent aae3461ddc
commit 162b925889
4 changed files with 239 additions and 74 deletions
+149 -66
View File
@@ -10,34 +10,40 @@ import {
unsafeCSS, unsafeCSS,
} from 'lit-element'; } from 'lit-element';
import { HomeAssistant, fireEvent, LovelaceCardEditor } from 'custom-card-helpers'; import { HomeAssistant, fireEvent, LovelaceCardEditor } from 'custom-card-helpers';
import { localize } from './localize/localize';
import { FrigateCardConfig } from './types'; import type { FrigateCardConfig } from './types';
import frigate_card_editor_style from './frigate-hass-card-editor.scss'; import frigate_card_editor_style from './frigate-hass-card-editor.scss';
const options = { const options = {
required: { required: {
icon: 'cog', icon: 'cog',
name: 'Required', name: localize('editor.required'),
secondary: 'Required options for this card to function', secondary: localize('editor.required_secondary'),
show: true, show: true,
}, },
optional: { optional: {
icon: 'tune', icon: 'tune',
name: 'Optional', name: localize('editor.optional'),
secondary: 'Optional configuration to tweak card behavior', secondary: localize('editor.optional_secondary'),
show: false,
},
appearance: {
icon: 'palette',
name: localize('editor.appearance'),
secondary: localize('editor.appearance_secondary'),
show: false, show: false,
}, },
webrtc: { webrtc: {
icon: 'webrtc', icon: 'webrtc',
name: 'WebRTC', name: localize('editor.webrtc'),
secondary: 'Optional WebRTC parameters', secondary: localize('editor.webrtc_secondary'),
show: false, show: false,
}, },
advanced: { advanced: {
icon: 'cogs', icon: 'cogs',
name: 'Advanced', name: localize('editor.advanced'),
secondary: 'Advanced options', secondary: localize('editor.advanced_secondary'),
show: false, show: false,
}, },
}; };
@@ -51,6 +57,9 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
private _initialized = false; private _initialized = false;
public setConfig(config: FrigateCardConfig): void { public setConfig(config: FrigateCardConfig): void {
// Note: This does not use Zod to parse the configuration, so it may be
// partially or completely invalid. It's more useful to have a partially
// valid configuration here, to allow the user to fix the broken parts.
this._config = config; this._config = config;
this.loadCardHelpers(); this.loadCardHelpers();
} }
@@ -96,33 +105,32 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
const viewModes = { const viewModes = {
'': '', '': '',
live: 'Live view', live: localize('menu.live'),
clips: 'Clip gallery', clips: localize('menu.clips'),
snapshots: 'Snapshot gallery', snapshots: localize('menu.snapshots'),
clip: 'Latest clip', clip: localize('menu.clip'),
snapshot: 'Latest snapshot', snapshot: localize('menu.snapshot'),
}; };
const menuModes = { const menuModes = {
'': '', '': '',
'hidden-top': 'Hidden Top', 'hidden-top': localize('menu_mode.hidden-top'),
'hidden-left': 'Hidden Left', 'hidden-left': localize('menu_mode.hidden-left'),
'hidden-bottom': 'Hidden Bottom', 'hidden-bottom': localize('menu_mode.hidden-bottom'),
'hidden-right': 'Hidden Right', 'hidden-right': localize('menu_mode.hidden-right'),
'overlay-top': 'Overlay Top', 'overlay-top': localize('menu_mode.overlay-top'),
'overlay-left': 'Overlay Left', 'overlay-left': localize('menu_mode.overlay-left'),
'overlay-bottom': 'Overlay Bottom', 'overlay-bottom': localize('menu_mode.overlay-bottom'),
'overlay-right': 'Overlay Right', 'overlay-right': localize('menu_mode.overlay-right'),
above: 'Above', above: localize('menu_mode.above'),
below: 'Below', below: localize('menu_mode.below'),
}; };
const liveProvider = { const liveProvider = {
'': '', '': '',
frigate: 'Frigate', frigate: localize('live_provider.frigate'),
webrtc: 'WebRTC', webrtc: localize('live_provider.webrtc'),
}; };
return html` return html`
<div class="card-config"> <div class="card-config">
<div class="option" @click=${this._toggleOption} .option=${'required'}> <div class="option" @click=${this._toggleOption} .option=${'required'}>
@@ -136,7 +144,7 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
? html` ? html`
<div class="values"> <div class="values">
<paper-dropdown-menu <paper-dropdown-menu
label="Camera Entity (Required)" label=${localize('editor.camera_entity')}
@value-changed=${this._valueChanged} @value-changed=${this._valueChanged}
.configValue=${'camera_entity'} .configValue=${'camera_entity'}
> >
@@ -164,7 +172,7 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
${options.optional.show ${options.optional.show
? html` <div class="values"> ? html` <div class="values">
<paper-dropdown-menu <paper-dropdown-menu
label="Motion Entity (Optional)" label=${localize('editor.motion_entity')}
@value-changed=${this._valueChanged} @value-changed=${this._valueChanged}
.configValue=${'motion_entity'} .configValue=${'motion_entity'}
> >
@@ -180,13 +188,13 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
</paper-listbox> </paper-listbox>
</paper-dropdown-menu> </paper-dropdown-menu>
<paper-input <paper-input
label="Frigate camera name (Optional)" label=${localize('editor.frigate_camera_name')}
.value=${this._config?.frigate_camera_name || ''} .value=${this._config?.frigate_camera_name || ''}
.configValue=${'frigate_camera_name'} .configValue=${'frigate_camera_name'}
@value-changed=${this._valueChanged} @value-changed=${this._valueChanged}
></paper-input> ></paper-input>
<paper-dropdown-menu <paper-dropdown-menu
label="Default view (Optional)" label=${localize('editor.default_view')}
@value-changed=${this._valueChanged} @value-changed=${this._valueChanged}
.configValue=${'view_default'} .configValue=${'view_default'}
> >
@@ -203,8 +211,47 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
})} })}
</paper-listbox> </paper-listbox>
</paper-dropdown-menu> </paper-dropdown-menu>
<paper-dropdown-menu <paper-input
label="Menu mode (Optional)" label=${localize('editor.frigate_client_id')}
.value=${this._config?.frigate_client_id || ''}
.configValue=${'frigate_client_id'}
@value-changed=${this._valueChanged}
></paper-input>
<paper-input
label=${localize('editor.view_timeout')}
prevent-invalid-input
allowed-pattern="[0-9]"
.value=${this._config?.view_timeout
? String(this._config.view_timeout)
: ''}
.configValue=${'view_timeout'}
@value-changed=${this._valueChanged}
></paper-input>
<paper-input
label=${localize('editor.frigate_url')}
.value=${this._config?.frigate_url || ''}
.configValue=${'frigate_url'}
@value-changed=${this._valueChanged}
></paper-input>
<ha-formfield .label=${localize('editor.autoplay_clip')}>
<ha-switch
.checked=${this._config?.autoplay_clip === true}
.configValue=${'autoplay_clip'}
@change=${this._valueChanged}
></ha-switch>
</ha-formfield>
</div>`
: ''}
<div class="option" @click=${this._toggleOption} .option=${'appearance'}>
<div class="row">
<ha-icon .icon=${`mdi:${options.appearance.icon}`}></ha-icon>
<div class="title">${options.appearance.name}</div>
</div>
<div class="secondary">${options.appearance.secondary}</div>
</div>
${options.appearance.show
? html`<paper-dropdown-menu
.label=${localize('editor.menu_mode')}
@value-changed=${this._valueChanged} @value-changed=${this._valueChanged}
.configValue=${'menu_mode'} .configValue=${'menu_mode'}
> >
@@ -219,38 +266,74 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
<paper-item .label="${key}"> ${menuModes[key]} </paper-item> <paper-item .label="${key}"> ${menuModes[key]} </paper-item>
`; `;
})} })}
</paper-listbox> </paper-listbox> </paper-dropdown-menu
</paper-dropdown-menu> ><br />
<paper-input <ha-formfield
label="Frigate client id (Optional, for >1 Frigate server)" .label=${localize('editor.show_button') +
.value=${this._config?.frigate_client_id || ''} ': ' +
.configValue=${'frigate_client_id'} localize('menu.frigate')}
@value-changed=${this._valueChanged} >
></paper-input>
<paper-input
label="View timeout (seconds)"
prevent-invalid-input
allowed-pattern="[0-9]"
.value=${this._config?.view_timeout
? String(this._config.view_timeout)
: ''}
.configValue=${'view_timeout'}
@value-changed=${this._valueChanged}
></paper-input>
<paper-input
label="Frigate URL (Optional, for Frigate UI button)"
.value=${this._config?.frigate_url || ''}
.configValue=${'frigate_url'}
@value-changed=${this._valueChanged}
></paper-input>
<ha-formfield .label=${`Autoplay latest clip`}>
<ha-switch <ha-switch
.checked=${this._config?.autoplay_clip === true} .checked=${this._config?.menu_buttons?.frigate ?? true}
.configValue=${'autoplay_clip'} .configValue=${'menu_buttons.frigate'}
@change=${this._valueChanged} @change=${this._valueChanged}
></ha-switch> ></ha-switch>
</ha-formfield> </ha-formfield>
</div>` <br />
<ha-formfield
.label=${localize('editor.show_button') + ': ' + localize('menu.live')}
>
<ha-switch
.checked=${this._config?.menu_buttons?.live ?? true}
.configValue=${'menu_buttons.live'}
@change=${this._valueChanged}
></ha-switch>
</ha-formfield>
<br />
<ha-formfield
.label=${localize('editor.show_button') + ': ' + localize('menu.clips')}
>
<ha-switch
.checked=${this._config?.menu_buttons?.clips ?? true}
.configValue=${'menu_buttons.clips'}
@change=${this._valueChanged}
></ha-switch>
</ha-formfield>
<br />
<ha-formfield
.label=${localize('editor.show_button') +
': ' +
localize('menu.snapshots')}
>
<ha-switch
.checked=${this._config?.menu_buttons?.snapshots ?? true}
.configValue=${'menu_buttons.snapshots'}
@change=${this._valueChanged}
></ha-switch>
</ha-formfield>
<br />
<ha-formfield
.label=${localize('editor.show_button') +
': ' +
localize('menu.frigate_ui')}
>
<ha-switch
.checked=${this._config?.menu_buttons?.frigate_ui ?? true}
.configValue=${'menu_buttons.frigate_ui'}
@change=${this._valueChanged}
></ha-switch>
</ha-formfield>
<br />
<ha-formfield
.label=${localize('editor.show_button') + ': ' + localize('menu.motion')}
>
<ha-switch
.checked=${this._config?.menu_buttons?.frigate_ui ?? true}
.configValue=${'menu_buttons.motion'}
@change=${this._valueChanged}
></ha-switch>
</ha-formfield>
<br /> `
: ''} : ''}
<div class="option" @click=${this._toggleOption} .option=${'advanced'}> <div class="option" @click=${this._toggleOption} .option=${'advanced'}>
<div class="row"> <div class="row">
@@ -262,7 +345,7 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
${options.advanced.show ${options.advanced.show
? html` <div class="values"> ? html` <div class="values">
<paper-input <paper-input
label="Frigate zone filter (Optional)" .label=${localize('editor.zone')}
.value=${this._config?.zone || ''} .value=${this._config?.zone || ''}
.configValue=${'zone'} .configValue=${'zone'}
@value-changed=${this._valueChanged} @value-changed=${this._valueChanged}
@@ -270,7 +353,7 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
</div> </div>
<div class="values"> <div class="values">
<paper-input <paper-input
label="Frigate label/object filter (Optional)" .label=${localize('editor.label')}
.value=${this._config?.label || ''} .value=${this._config?.label || ''}
.configValue=${'label'} .configValue=${'label'}
@value-changed=${this._valueChanged} @value-changed=${this._valueChanged}
@@ -287,7 +370,7 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
${options.webrtc.show ${options.webrtc.show
? html` <div class="values"> ? html` <div class="values">
<paper-dropdown-menu <paper-dropdown-menu
label="WebRTC/Frigate Live provider (Optional)" .label=${localize('editor.live_provider')}
@value-changed=${this._valueChanged} @value-changed=${this._valueChanged}
.configValue=${'live_provider'} .configValue=${'live_provider'}
> >
@@ -305,7 +388,7 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
</paper-listbox> </paper-listbox>
</paper-dropdown-menu> </paper-dropdown-menu>
<paper-dropdown-menu <paper-dropdown-menu
label="WebRTC Camera Entity (To use with WebRTC Live Provider)" .label=${localize('webrtc.entity')}
@value-changed=${this._valueChanged} @value-changed=${this._valueChanged}
.configValue=${'webrtc.entity'} .configValue=${'webrtc.entity'}
> >
+16 -3
View File
@@ -180,6 +180,12 @@ export class FrigateCardMenu extends LitElement {
bottom: this.menuMode.endsWith('-bottom'), bottom: this.menuMode.endsWith('-bottom'),
}; };
// If the menu is in hidden mode and there's no button to unhide it, just
// show nothing.
if (this.menuMode.startsWith('hidden-') && !this.buttons.get('frigate')) {
return html``;
}
return html` return html`
<div class=${classMap(classes)}> <div class=${classMap(classes)}>
${Array.from(this.buttons.keys()).map((name) => { ${Array.from(this.buttons.keys()).map((name) => {
@@ -295,15 +301,22 @@ export class FrigateCard extends LitElement {
protected _getMenuButtons(): Map<string, MenuButton> { protected _getMenuButtons(): Map<string, MenuButton> {
const buttons: Map<string, MenuButton> = new Map(); const buttons: Map<string, MenuButton> = new Map();
if (this.config.menu_buttons?.frigate) {
buttons.set('frigate', { description: 'Frigate Menu' }); buttons.set('frigate', { description: 'Frigate Menu' });
}
if (this.config.menu_buttons?.live) {
buttons.set('live', { icon: 'mdi:cctv', description: 'View Live' }); buttons.set('live', { icon: 'mdi:cctv', description: 'View Live' });
}
if (this.config.menu_buttons?.clips) {
buttons.set('clips', { icon: 'mdi:filmstrip', description: 'View Clips' }); buttons.set('clips', { icon: 'mdi:filmstrip', description: 'View Clips' });
}
if (this.config.menu_buttons?.snapshots) {
buttons.set('snapshots', { icon: 'mdi:camera', description: 'View Snapshots' }); buttons.set('snapshots', { icon: 'mdi:camera', description: 'View Snapshots' });
if (this.config.frigate_url) { }
if (this.config.menu_buttons?.frigate_ui && this.config.frigate_url) {
buttons.set('frigate_ui', { icon: 'mdi:web', description: 'View Frigate UI' }); buttons.set('frigate_ui', { icon: 'mdi:web', description: 'View Frigate UI' });
} }
if (this.config.menu_buttons?.motion && this._hass && this.config.motion_entity) {
if (this._hass && this.config.motion_entity) {
const on = this._hass.states[this.config.motion_entity]?.state == 'on'; const on = this._hass.states[this.config.motion_entity]?.state == 'on';
const motionIcon = on ? 'mdi:motion-sensor' : 'mdi:walk'; const motionIcon = on ? 'mdi:motion-sensor' : 'mdi:walk';
+54
View File
@@ -5,5 +5,59 @@
"missing_webrtc": "WebRTC component not found", "missing_webrtc": "WebRTC component not found",
"show_warning": "Show Warning", "show_warning": "Show Warning",
"show_error": "Show Error" "show_error": "Show Error"
},
"editor": {
"required": "Required",
"required_secondary": "Required options for this card to function",
"optional": "Optional",
"optional_secondary": "Optional configuration to tweak card behavior",
"appearance": "Appearance",
"appearance_secondary": "Appearance of the card",
"webrtc": "WebRTC",
"webrtc_secondary": "Optional WebRTC parameters",
"advanced": "Advanced",
"advanced_secondary": "Advanced options",
"camera_entity": "Camera Entity (Required)",
"motion_entity": "Motion Entity (Optional)",
"frigate_camera_name": "Frigate camera name (Optional)",
"default_view": "Default view (Optional)",
"frigate_client_id": "Frigate client id (Optional, for >1 Frigate server)",
"view_timeout": "View timeout (seconds)",
"frigate_url": "Frigate URL (Optional, for Frigate UI button)",
"autoplay_clip": "Autoplay latest clip",
"menu_mode": "Menu mode (Optional)",
"show_button": "Show button",
"zone": "Zone",
"label": "Frigate label/object filter (Optional)",
"live_provider": "Live view provider (Optional)"
},
"menu": {
"frigate": "Frigate",
"live": "Live View",
"clips": "Clips Gallery",
"snapshots": "Snapshots Gallery",
"clip": "Latest Clip",
"snapshot": "Latest Snapshot",
"frigate_ui": "Frigate User Interface",
"motion": "Motion Entity"
},
"menu_mode": {
"hidden-top": "Hidden Top",
"hidden-left": "Hidden Left",
"hidden-bottom": "Hidden Bottom",
"hidden-right": "Hidden Right",
"overlay-top": "Overlay Top",
"overlay-left": "Overlay Left",
"overlay-bottom": "Overlay Bottom",
"overlay-right": "Overlay Right",
"above": "Above",
"below": "Below"
},
"live_provider": {
"frigate": "Frigate",
"webrtc": "WebRTC"
},
"webrtc": {
"entity": "WebRTC Camera Entity (To use with WebRTC Live Provider)"
} }
} }
+15
View File
@@ -62,6 +62,21 @@ export const frigateCardConfigSchema = z.object({
zone: z.string().optional(), zone: z.string().optional(),
autoplay_clip: z.boolean().default(false), autoplay_clip: z.boolean().default(false),
menu_mode: z.enum(FRIGATE_MENU_MODES).optional().default('hidden-top'), menu_mode: z.enum(FRIGATE_MENU_MODES).optional().default('hidden-top'),
menu_buttons: z.object({
frigate: z.boolean().default(true),
live: z.boolean().default(true),
clips: z.boolean().default(true),
snapshots: z.boolean().default(true),
frigate_ui: z.boolean().default(true),
motion: z.boolean().default(true),
}).default({
frigate: true,
live: true,
clips: true,
snapshots: true,
frigate_ui: true,
motion: true,
}),
// Stock lovelace card config. // Stock lovelace card config.
type: z.string(), type: z.string(),