First attempt to add state to frigate card conditions.

This commit is contained in:
Dermot Duffy
2022-04-30 21:39:29 -07:00
parent d273328e76
commit dafaae7fe4
5 changed files with 85 additions and 63 deletions
+52 -51
View File
@@ -1,6 +1,5 @@
import { LitElement, TemplateResult, html, CSSResultGroup, unsafeCSS } from 'lit';
import { HomeAssistant } from 'custom-card-helpers';
import { createRef, ref, Ref } from 'lit/directives/ref.js';
import { customElement, property } from 'lit/decorators.js';
import {
@@ -11,10 +10,7 @@ import {
PictureElements,
MenuSubmenu,
} from '../types.js';
import {
dispatchErrorMessageEvent,
dispatchFrigateCardEvent,
} from '../common.js';
import { dispatchErrorMessageEvent, dispatchFrigateCardEvent } from '../common.js';
import elementsStyle from '../scss/elements.scss';
import { localize } from '../localize/localize.js';
@@ -53,6 +49,11 @@ import { ConditionState, fetchStateAndEvaluateCondition } from '../card-conditio
* upper layers to handle correctly.
*/
interface HuiConditionalElement extends HTMLElement {
hass: HomeAssistant;
setConfig(config: unknown): void;
}
// A small wrapper around a HA conditional element used to render a set of
// picture elements.
@customElement('frigate-card-elements-core')
@@ -67,19 +68,10 @@ class FrigateCardElementsCore extends LitElement {
@property({ attribute: false })
protected conditionState?: ConditionState;
protected _root: HTMLElement | null = null;
protected _hass?: HomeAssistant;
protected _root: HuiConditionalElement | null = null;
/**
* Set Home Assistant object.
*/
set hass(hass: HomeAssistant) {
if (this._root) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(this._root as any).hass = hass;
}
this._hass = hass;
}
@property({ attribute: false })
protected hass?: HomeAssistant;
/**
* Create a transparent render root.
@@ -90,17 +82,16 @@ class FrigateCardElementsCore extends LitElement {
/**
* Create the root node for our picture elements.
* @returns
* @returns The newly created root.
*/
protected _createRoot(): HTMLElement {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const elementConstructor = customElements.get('hui-conditional-element') as any;
if (!elementConstructor || !this._hass) {
protected _createRoot(): HuiConditionalElement {
const elementConstructor = customElements.get('hui-conditional-element');
if (!elementConstructor || !this.hass) {
throw new Error(localize('error.could_not_render_elements'));
}
const element = new elementConstructor();
element.hass = this._hass;
const element = new elementConstructor() as HuiConditionalElement;
element.hass = this.hass;
const config = {
type: 'conditional',
conditions: [],
@@ -115,20 +106,37 @@ class FrigateCardElementsCore extends LitElement {
return element;
}
/**
* Create the root as necessary prior to rendering.
*/
protected willUpdate(): void {
try {
// The root is only created once, to avoid the elements being continually
// re-created & destroyed (for some elements, e.g. image, this would
// otherwise cause serious flickering).
if (!this._root) {
this._root = this._createRoot();
}
} catch (e) {
return dispatchErrorMessageEvent(this, (e as Error).message);
}
}
/**
* Render the elements.
* @returns A rendered template or void.
*/
protected render(): TemplateResult | void {
try {
// Recreate the root on each render to ensure conditional ancestors
// re-fire events as necessary.
this._root = this._createRoot();
} catch (e) {
return dispatchErrorMessageEvent(this, (e as Error).message);
}
return html`${this._root || ''}`;
}
protected updated(): void {
if (this.hass && this._root) {
// Always update hass. It is used as a trigger to re-evaluate conditions
// down the chain, see the note on FrigateCardElementsConditional.
this._root.hass = this.hass;
}
}
}
/**
@@ -145,6 +153,8 @@ export class FrigateCardElements extends LitElement {
@property({ attribute: false })
protected conditionState?: ConditionState;
protected _boundMenuRemoveHandler = this._menuRemoveHandler.bind(this);
/**
* Handle a picture element to be removed from the menu.
* @param ev The event.
@@ -175,13 +185,10 @@ export class FrigateCardElements extends LitElement {
// Ensure listener is only attached 1 time by removing it first.
path[0].removeEventListener(
'frigate-card:menu-remove',
this._menuRemoveHandler.bind(this),
this._boundMenuRemoveHandler,
);
path[0].addEventListener(
'frigate-card:menu-remove',
this._menuRemoveHandler.bind(this),
);
path[0].addEventListener('frigate-card:menu-remove', this._boundMenuRemoveHandler);
}
/**
@@ -232,18 +239,13 @@ export class FrigateCardElements extends LitElement {
@customElement('frigate-card-conditional')
export class FrigateCardElementsConditional extends LitElement {
protected _config?: FrigateConditional;
protected _hass?: HomeAssistant;
protected _refCore: Ref<FrigateCardElementsCore> = createRef() ;
/**
* Set the Home Assistant object.
*/
set hass(hass: HomeAssistant) {
if (this._refCore.value) {
this._refCore.value.hass = hass;
}
this._hass = hass;
}
// Every set of hass is treated as a reason to re-evaluate. Given that this
// node may be buried down the DOM (as a descendent of non-Frigate card
// elements), the hass object is used as the (only) trigger for condition
// re-fetch even if hass itself has not changed.
@property({ attribute: false, hasChanged: () => true })
protected hass?: HomeAssistant;
/**
* Set the card configuration.
@@ -255,7 +257,7 @@ export class FrigateCardElementsConditional extends LitElement {
/**
* Create a root into which to render. This card is "transparent".
* @returns
* @returns
*/
createRenderRoot(): LitElement {
return this;
@@ -279,8 +281,7 @@ export class FrigateCardElementsConditional extends LitElement {
protected render(): TemplateResult | void {
if (fetchStateAndEvaluateCondition(this, this._config.conditions)) {
return html` <frigate-card-elements-core
${ref(this._refCore)}
.hass=${this._hass}
.hass=${this.hass}
.elements=${this._config.elements}
>
</frigate-card-elements-core>`;
@@ -329,4 +330,4 @@ export class FrigateCardElementsMenuIcon extends FrigateCardElementsBaseMenuIcon
export class FrigateCardElementsMenuStateIcon extends FrigateCardElementsBaseMenuIcon<MenuStateIcon> {}
@customElement('frigate-card-menu-submenu')
export class FrigateCardElementsMenuSubmenu extends FrigateCardElementsBaseMenuIcon<MenuSubmenu> {}
export class FrigateCardElementsMenuSubmenu extends FrigateCardElementsBaseMenuIcon<MenuSubmenu> {}
+2 -2
View File
@@ -395,10 +395,10 @@ export class FrigateCardLiveCarousel extends FrigateCardMediaCarousel {
// The conditionState object contains the currently live camera, which (in
// the carousel for example) is not necessarily the live camera this
// <frigate-card-live-provider> is rendering right now.
const conditionState = Object.assign({
const conditionState = {
...this.conditionState,
camera: camera,
});
};
const config = getOverriddenConfig(
this.liveConfig,