From cfcfd0746755d8b570497cdb1c5462c2dddd2dec Mon Sep 17 00:00:00 2001 From: Dermot Duffy Date: Sat, 8 Jan 2022 19:30:18 -0800 Subject: [PATCH] Add menu overrides and remove menu conditions. --- src/card.ts | 122 +++++++++++++++++++++++------------------ src/components/menu.ts | 13 +---- src/types.ts | 31 +++++++++-- 3 files changed, 97 insertions(+), 69 deletions(-) diff --git a/src/card.ts b/src/card.ts index 753d2789..8892c0a1 100644 --- a/src/card.ts +++ b/src/card.ts @@ -133,13 +133,11 @@ export class FrigateCard extends LitElement { protected _hass?: HomeAssistant & ExtendedHomeAssistant; @state() - public config!: FrigateCardConfig; + public _baseConfig!: FrigateCardConfig; @state() public _overriddenConfig?: FrigateCardConfig; - protected _interactionTimerID: number | null = null; - @property({ attribute: false }) protected _view?: View; @@ -152,6 +150,9 @@ export class FrigateCard extends LitElement { @query('frigate-card-elements') _elements?: FrigateCardElements; + // Human interaction timer ID. + protected _interactionTimerID: number | null = null; + // Whether or not media is actively playing (live or clip). protected _mediaPlaying = false; @@ -231,9 +232,10 @@ export class FrigateCard extends LitElement { }; this._overriddenConfig = getOverriddenConfig( - this.config, - this.config.overrides, - this._conditionState) as FrigateCardConfig; + this._baseConfig, + this._baseConfig.overrides, + this._conditionState, + ) as FrigateCardConfig; } /** @@ -275,7 +277,7 @@ export class FrigateCard extends LitElement { protected _getMenuButtons(): MenuButton[] { const buttons: MenuButton[] = []; - if (this.config.menu.buttons.frigate) { + if (this._getConfig().menu.buttons.frigate) { buttons.push( this._getFrigateCardMenuButton({ tap_action: 'frigate', @@ -288,7 +290,11 @@ export class FrigateCard extends LitElement { ); } - if (this.config.menu.buttons.cameras && this._cameras && this._cameras.size > 1) { + if ( + this._getConfig().menu.buttons.cameras && + this._cameras && + this._cameras.size > 1 + ) { const menuItems = Array.from(this._cameras, ([camera, config]) => { return { icon: getCameraIcon(this._hass, config), @@ -307,7 +313,7 @@ export class FrigateCard extends LitElement { }); } - if (this.config.menu.buttons.live) { + if (this._getConfig().menu.buttons.live) { buttons.push( this._getFrigateCardMenuButton({ tap_action: 'live', @@ -318,7 +324,7 @@ export class FrigateCard extends LitElement { ); } - if (this.config.menu.buttons.clips) { + if (this._getConfig().menu.buttons.clips) { buttons.push( this._getFrigateCardMenuButton({ tap_action: 'clips', @@ -329,7 +335,7 @@ export class FrigateCard extends LitElement { }), ); } - if (this.config.menu.buttons.snapshots) { + if (this._getConfig().menu.buttons.snapshots) { buttons.push( this._getFrigateCardMenuButton({ tap_action: 'snapshots', @@ -340,7 +346,7 @@ export class FrigateCard extends LitElement { }), ); } - if (this.config.menu.buttons.image) { + if (this._getConfig().menu.buttons.image) { buttons.push( this._getFrigateCardMenuButton({ tap_action: 'image', @@ -350,7 +356,7 @@ export class FrigateCard extends LitElement { }), ); } - if (this.config.menu.buttons.download && this._view?.isViewerView()) { + if (this._getConfig().menu.buttons.download && this._view?.isViewerView()) { buttons.push( this._getFrigateCardMenuButton({ tap_action: 'download', @@ -362,7 +368,7 @@ export class FrigateCard extends LitElement { const cameraConfig = this._getSelectedCameraConfig(); if ( - this.config.menu.buttons.frigate_ui && + this._getConfig().menu.buttons.frigate_ui && cameraConfig && cameraConfig.frigate_url ) { @@ -374,7 +380,7 @@ export class FrigateCard extends LitElement { }), ); } - if (this.config.menu.buttons.fullscreen && screenfull.isEnabled) { + if (this._getConfig().menu.buttons.fullscreen && screenfull.isEnabled) { buttons.push( this._getFrigateCardMenuButton({ tap_action: 'fullscreen', @@ -439,8 +445,8 @@ export class FrigateCard extends LitElement { } }; - if (this.config.cameras && Array.isArray(this.config.cameras)) { - await Promise.all(this.config.cameras.map(addCameraConfig.bind(this))); + if (this._getConfig().cameras && Array.isArray(this._getConfig().cameras)) { + await Promise.all(this._getConfig().cameras.map(addCameraConfig.bind(this))); } if (!cameras.size) { @@ -600,17 +606,25 @@ export class FrigateCard extends LitElement { getLovelace().setEditMode(true); } - this.config = config; + this._baseConfig = config; this._cameras = undefined; this._view = undefined; - if (this.config.view.update_force) { + if (this._getConfig().view.update_force) { // If update force is enabled, start a timer right away. this._resetInteractionTimer(); } this._changeView(); } + /** + * Card the card config, prioritizing the overriden config if present. + * @returns A FrigateCardConfig. + */ + protected _getConfig(): FrigateCardConfig { + return this._overriddenConfig || this._baseConfig; + } + protected _changeView(args?: { view?: View; resetMessage?: boolean }): void { console.info(`Request to change view: ${JSON.stringify(args?.view?.view)}`); @@ -626,7 +640,7 @@ export class FrigateCard extends LitElement { if (camera) { this._view = new View({ - view: this.config.view.default, + view: this._getConfig().view.default, camera: camera, }); this._generateConditionState(); @@ -651,10 +665,6 @@ export class FrigateCard extends LitElement { * @returns True if the card should be updated. */ protected shouldUpdate(changedProps: PropertyValues): boolean { - if (!this.config) { - return false; - } - if (changedProps.size > 1) { return true; } @@ -668,12 +678,12 @@ export class FrigateCard extends LitElement { // Assistant update if there's been recent interaction (e.g. clicks on the // card) or if there is media active playing. if ( - (this.config.view.update_force || + (this._getConfig().view.update_force || !(this._interactionTimerID && this._mediaPlaying)) && shouldUpdateBasedOnHass( this._hass, oldHass, - this.config.view.update_entities || [], + this._getConfig().view.update_entities || [], ) ) { // If entities being monitored have changed then reset the view to the @@ -868,18 +878,18 @@ export class FrigateCard extends LitElement { } protected _resetInteractionTimer(): void { - if (this.config.view.timeout) { + if (this._getConfig().view.timeout) { if (this._interactionTimerID) { window.clearTimeout(this._interactionTimerID); } this._interactionTimerID = window.setTimeout(() => { this._interactionTimerID = null; this._changeView(); - if (this.config.view.update_force) { + if (this._getConfig().view.update_force) { // If force is enabled, the timer just resets and starts over. this._resetInteractionTimer(); } - }, this.config.view.timeout * 1000); + }, this._getConfig().view.timeout * 1000); } } @@ -889,12 +899,12 @@ export class FrigateCard extends LitElement { */ protected _renderMenu(): TemplateResult | void { const classes = { - 'hover-menu': this.config.menu.mode.startsWith('hover-'), + 'hover-menu': this._getConfig().menu.mode.startsWith('hover-'), }; return html` - ${this.config.menu.mode == 'above' ? this._renderMenu() : ''} + ${this._getConfig().menu.mode == 'above' ? this._renderMenu() : ''}
- ${this.config.elements + ${this._getConfig().elements ? // Always show elements to allow for custom menu items (etc.) to // be present even if a particular view has an error. html` { this._addDynamicMenuButton(e.detail); @@ -1141,7 +1151,7 @@ export class FrigateCard extends LitElement {
- ${this.config.menu.mode != 'above' ? this._renderMenu() : ''} + ${this._getConfig().menu.mode != 'above' ? this._renderMenu() : ''} `; } @@ -1156,22 +1166,22 @@ export class FrigateCard extends LitElement { } const galleryClasses = { - hidden: this.config.live.preload && !this._view.isGalleryView(), + hidden: this._getConfig().live.preload && !this._view.isGalleryView(), }; const viewerClasses = { - hidden: this.config.live.preload && !this._view.isViewerView(), + hidden: this._getConfig().live.preload && !this._view.isViewerView(), }; const liveClasses = { - hidden: this.config.live.preload && this._view.view != 'live', + hidden: this._getConfig().live.preload && this._view.view != 'live', }; const imageClasses = { - hidden: this.config.live.preload && this._view.view != 'image', + hidden: this._getConfig().live.preload && this._view.view != 'image', }; return html` ${!this._message && this._view.is('image') ? html` ` @@ -1196,25 +1206,29 @@ export class FrigateCard extends LitElement { this._view, cameraConfig, )} - .viewerConfig=${this.config.event_viewer} + .viewerConfig=${this._getConfig().event_viewer} .resolvedMediaCache=${this._resolvedMediaCache} class="${classMap(viewerClasses)}" > ` : ``} ${ - // Note the subtle difference in condition below vs the other views in order + // Note: Subtle difference in condition below vs the other views in order // to always render the live view for live.preload mode. - (!this._message && this._view.is('live')) || this.config.live.preload + + // Note: uses the baseConfig rather than the + // overriden config, as it does it's own overriding as part of the + // camera carousel. + (!this._message && this._view.is('live')) || this._getConfig().live.preload ? html` diff --git a/src/components/menu.ts b/src/components/menu.ts index d2dda060..f493bfeb 100644 --- a/src/components/menu.ts +++ b/src/components/menu.ts @@ -6,7 +6,7 @@ import { html, unsafeCSS, } from 'lit'; -import { customElement, property } from 'lit/decorators.js'; +import { customElement, property, state } from 'lit/decorators.js'; import { classMap } from 'lit/directives/class-map.js'; import { styleMap } from 'lit/directives/style-map.js'; @@ -28,7 +28,6 @@ import { } from '../common.js'; import menuStyle from '../scss/menu.scss'; -import { ConditionState, evaluateCondition } from '../card-condition.js'; import { Corner } from '@material/mwc-menu'; export const FRIGATE_BUTTON_MENU_ICON = 'frigate'; @@ -41,13 +40,13 @@ export class FrigateCardMenu extends LitElement { @property({ attribute: false }) public hass?: HomeAssistant & ExtendedHomeAssistant; - @property({ attribute: false }) set menuConfig(menuConfig: MenuConfig) { this._menuConfig = menuConfig; if (menuConfig) { this.style.setProperty('--frigate-card-menu-button-size', menuConfig.button_size); } } + @state() protected _menuConfig?: MenuConfig; @property({ attribute: false }) @@ -56,9 +55,6 @@ export class FrigateCardMenu extends LitElement { @property({ attribute: false }) public buttons: MenuButton[] = []; - @property({ attribute: false }) - protected conditionState?: ConditionState; - /** * Handle an action on a menu button. * @param ev The action event. @@ -185,10 +181,7 @@ export class FrigateCardMenu extends LitElement { } const mode = this._menuConfig.mode; - if ( - mode == 'none' || - !evaluateCondition(this._menuConfig.conditions, this.conditionState) - ) { + if (mode == 'none') { return; } diff --git a/src/types.ts b/src/types.ts index 951588fc..34b6395a 100644 --- a/src/types.ts +++ b/src/types.ts @@ -572,9 +572,28 @@ const menuConfigDefault = { }, button_size: '40px', }; -const menuConfigSchema = z - .object({ - mode: z.enum(FRIGATE_MENU_MODES).optional().default(menuConfigDefault.mode), + +const menuOverridableConfigSchema = z.object({ + mode: z.enum(FRIGATE_MENU_MODES).optional(), + buttons: z + .object({ + frigate: z.boolean().optional(), + cameras: z.boolean().optional(), + live: z.boolean().optional(), + clips: z.boolean().optional(), + snapshots: z.boolean().optional(), + image: z.boolean().optional(), + download: z.boolean().optional(), + frigate_ui: z.boolean().optional(), + fullscreen: z.boolean().optional(), + }) + .optional(), + button_size: z.string().optional(), +}); + +const menuConfigSchema = menuOverridableConfigSchema + .extend({ + mode: menuOverridableConfigSchema.shape.mode.default(menuConfigDefault.mode), buttons: z .object({ frigate: z.boolean().default(menuConfigDefault.buttons.frigate), @@ -588,8 +607,9 @@ const menuConfigSchema = z fullscreen: z.boolean().default(menuConfigDefault.buttons.fullscreen), }) .default(menuConfigDefault.buttons), - button_size: z.string().default(menuConfigDefault.button_size), - conditions: frigateCardConditionSchema.optional(), + button_size: menuOverridableConfigSchema.shape.button_size.default( + menuConfigDefault.button_size, + ) }) .default(menuConfigDefault); export type MenuConfig = z.infer; @@ -688,6 +708,7 @@ const dimensionsConfigSchema = z */ const overrideConfigurationSchema = z.object({ live: liveOverridableConfigSchema.optional(), + menu: menuOverridableConfigSchema.optional(), }); export type OverrideConfigurationKey = keyof z.infer;