Add consistent initialization logic to improve Chromecast support.
This commit is contained in:
+17
@@ -57,6 +57,7 @@ import {
|
|||||||
homeAssistantWSRequest,
|
homeAssistantWSRequest,
|
||||||
isValidMediaShowInfo,
|
isValidMediaShowInfo,
|
||||||
shouldUpdateBasedOnHass,
|
shouldUpdateBasedOnHass,
|
||||||
|
sideLoadHomeAssistantElements,
|
||||||
} from './common.js';
|
} from './common.js';
|
||||||
import { localize } from './localize/localize.js';
|
import { localize } from './localize/localize.js';
|
||||||
import { renderMessage, renderProgressIndicator } from './components/message.js';
|
import { renderMessage, renderProgressIndicator } from './components/message.js';
|
||||||
@@ -186,6 +187,9 @@ export class FrigateCard extends LitElement {
|
|||||||
// per second for performance reasons.
|
// per second for performance reasons.
|
||||||
protected _boundMouseHandler = throttle(this._mouseHandler.bind(this), 1 * 1000);
|
protected _boundMouseHandler = throttle(this._mouseHandler.bind(this), 1 * 1000);
|
||||||
|
|
||||||
|
// Whether the card has been successfully initialized.
|
||||||
|
protected _initialized = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the Home Assistant object.
|
* Set the Home Assistant object.
|
||||||
*/
|
*/
|
||||||
@@ -830,6 +834,19 @@ export class FrigateCard extends LitElement {
|
|||||||
this._changeView({ view: e.detail });
|
this._changeView({ view: e.detail });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called before each update.
|
||||||
|
*/
|
||||||
|
protected willUpdate(): void {
|
||||||
|
if (!this._initialized) {
|
||||||
|
sideLoadHomeAssistantElements().then((success) => {
|
||||||
|
if (success) {
|
||||||
|
this._initialized = true;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine whether the element should be updated.
|
* Determine whether the element should be updated.
|
||||||
* @param changedProps The changed properties if any.
|
* @param changedProps The changed properties if any.
|
||||||
|
|||||||
+50
-9
@@ -21,6 +21,7 @@ import {
|
|||||||
ActionsConfig,
|
ActionsConfig,
|
||||||
ActionType,
|
ActionType,
|
||||||
CameraConfig,
|
CameraConfig,
|
||||||
|
CardHelpers,
|
||||||
ExtendedHomeAssistant,
|
ExtendedHomeAssistant,
|
||||||
FrigateCardAction,
|
FrigateCardAction,
|
||||||
FrigateCardCustomAction,
|
FrigateCardCustomAction,
|
||||||
@@ -33,7 +34,7 @@ import {
|
|||||||
signedPathSchema,
|
signedPathSchema,
|
||||||
StateParameters,
|
StateParameters,
|
||||||
} from './types.js';
|
} from './types.js';
|
||||||
import { stateIcon } from './icons/state-icon.js'
|
import { stateIcon } from './icons/state-icon.js';
|
||||||
|
|
||||||
const MEDIA_INFO_HEIGHT_CUTOFF = 50;
|
const MEDIA_INFO_HEIGHT_CUTOFF = 50;
|
||||||
const MEDIA_INFO_WIDTH_CUTOFF = MEDIA_INFO_HEIGHT_CUTOFF;
|
const MEDIA_INFO_WIDTH_CUTOFF = MEDIA_INFO_HEIGHT_CUTOFF;
|
||||||
@@ -290,10 +291,10 @@ export function convertActionToFrigateCardCustomAction(
|
|||||||
export function createFrigateCardCustomAction(
|
export function createFrigateCardCustomAction(
|
||||||
action: FrigateCardAction,
|
action: FrigateCardAction,
|
||||||
args?: {
|
args?: {
|
||||||
camera?: string,
|
camera?: string;
|
||||||
media_player?: string,
|
media_player?: string;
|
||||||
media_player_action?: 'play' | 'stop',
|
media_player_action?: 'play' | 'stop';
|
||||||
}
|
},
|
||||||
): FrigateCardCustomAction | null {
|
): FrigateCardCustomAction | null {
|
||||||
if (action === 'camera_select') {
|
if (action === 'camera_select') {
|
||||||
if (!args?.camera) {
|
if (!args?.camera) {
|
||||||
@@ -473,10 +474,7 @@ export function getEntityTitle(
|
|||||||
* @param hass The Home Assistant object.
|
* @param hass The Home Assistant object.
|
||||||
* @returns The icon or undefined.
|
* @returns The icon or undefined.
|
||||||
*/
|
*/
|
||||||
export function getEntityIcon(
|
export function getEntityIcon(hass?: HomeAssistant, entity?: string): string {
|
||||||
hass?: HomeAssistant,
|
|
||||||
entity?: string,
|
|
||||||
): string {
|
|
||||||
return stateIcon(entity ? hass?.states[entity] : null);
|
return stateIcon(entity ? hass?.states[entity] : null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -682,3 +680,46 @@ export function getEventDurationString(event: FrigateEvent): string {
|
|||||||
duration += `${seconds}s`;
|
duration += `${seconds}s`;
|
||||||
return duration;
|
return duration;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Side loads the HA elements this card needs. This trickery is unfortunate
|
||||||
|
* necessary, see:
|
||||||
|
* - https://github.com/thomasloven/hass-config/wiki/PreLoading-Lovelace-Elements
|
||||||
|
* @returns `true` if the load is successful, `false` otherwise.
|
||||||
|
*/
|
||||||
|
export const sideLoadHomeAssistantElements = async (): Promise<boolean> => {
|
||||||
|
const neededElements = [
|
||||||
|
'ha-selector',
|
||||||
|
'ha-menu-button',
|
||||||
|
'ha-camera-stream',
|
||||||
|
'ha-hls-player',
|
||||||
|
'ha-web-rtc-player',
|
||||||
|
'ha-icon',
|
||||||
|
'ha-circular-progress',
|
||||||
|
'ha-icon-button',
|
||||||
|
'ha-card',
|
||||||
|
'ha-svg-icon',
|
||||||
|
'ha-button-menu',
|
||||||
|
];
|
||||||
|
|
||||||
|
if (neededElements.every((element) => customElements.get(element))) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
const helpers: CardHelpers = await (window as any).loadCardHelpers();
|
||||||
|
|
||||||
|
// The picture-glance editor loads everything this card needs. In particular:
|
||||||
|
//
|
||||||
|
// See: https://github.com/thomasloven/hass-config/wiki/PreLoading-Lovelace-Elements
|
||||||
|
const pictureGlance = await helpers.createCardElement({
|
||||||
|
type: 'picture-glance',
|
||||||
|
entities: [],
|
||||||
|
camera_image: 'dummy-to-load-editor-components',
|
||||||
|
});
|
||||||
|
if (pictureGlance.constructor.getConfigElement) {
|
||||||
|
await pictureGlance.constructor.getConfigElement();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|||||||
+16
-39
@@ -1,4 +1,3 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
||||||
import { CSSResultGroup, LitElement, TemplateResult, html, unsafeCSS } from 'lit';
|
import { CSSResultGroup, LitElement, TemplateResult, html, unsafeCSS } from 'lit';
|
||||||
import { customElement, property, state } from 'lit/decorators.js';
|
import { customElement, property, state } from 'lit/decorators.js';
|
||||||
|
|
||||||
@@ -90,7 +89,12 @@ import {
|
|||||||
CONF_VIEW_UPDATE_FORCE,
|
CONF_VIEW_UPDATE_FORCE,
|
||||||
CONF_VIEW_UPDATE_SECONDS,
|
CONF_VIEW_UPDATE_SECONDS,
|
||||||
} from './const.js';
|
} from './const.js';
|
||||||
import { arrayMove, getCameraID, getCameraTitle } from './common.js';
|
import {
|
||||||
|
arrayMove,
|
||||||
|
getCameraID,
|
||||||
|
getCameraTitle,
|
||||||
|
sideLoadHomeAssistantElements,
|
||||||
|
} from './common.js';
|
||||||
import {
|
import {
|
||||||
copyConfig,
|
copyConfig,
|
||||||
deleteConfigValue,
|
deleteConfigValue,
|
||||||
@@ -183,7 +187,6 @@ const options: EditorOptions = {
|
|||||||
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() protected _config?: RawFrigateCardConfig;
|
@state() protected _config?: RawFrigateCardConfig;
|
||||||
@state() protected _helpers?: any;
|
|
||||||
protected _initialized = false;
|
protected _initialized = false;
|
||||||
protected _configUpgradeable = false;
|
protected _configUpgradeable = false;
|
||||||
|
|
||||||
@@ -386,15 +389,19 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
|||||||
// such, RawFrigateCardConfig is used as the type.
|
// such, RawFrigateCardConfig is used as the type.
|
||||||
this._config = config;
|
this._config = config;
|
||||||
this._configUpgradeable = isConfigUpgradeable(config);
|
this._configUpgradeable = isConfigUpgradeable(config);
|
||||||
this.loadCardHelpers();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected shouldUpdate(): boolean {
|
/**
|
||||||
|
* Called before each update.
|
||||||
|
*/
|
||||||
|
protected willUpdate(): void {
|
||||||
if (!this._initialized) {
|
if (!this._initialized) {
|
||||||
this._initialize();
|
sideLoadHomeAssistantElements().then((success) => {
|
||||||
|
if (success) {
|
||||||
|
this._initialized = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected _getEntities(domain: string): string[] {
|
protected _getEntities(domain: string): string[] {
|
||||||
@@ -929,7 +936,7 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected render(): TemplateResult | void {
|
protected render(): TemplateResult | void {
|
||||||
if (!this.hass || !this._helpers || !this._config) {
|
if (!this.hass || !this._config) {
|
||||||
return html``;
|
return html``;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1219,36 +1226,6 @@ export class FrigateCardEditor extends LitElement implements LovelaceCardEditor
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Verify editor is initialized.
|
|
||||||
*/
|
|
||||||
protected _initialize(): void {
|
|
||||||
if (this.hass === undefined) return;
|
|
||||||
if (this._config === undefined) return;
|
|
||||||
if (this._helpers === undefined) return;
|
|
||||||
|
|
||||||
(async (): Promise<void> => {
|
|
||||||
// The picture-glance editor loads the ha-selectors.
|
|
||||||
// See: https://github.com/thomasloven/hass-config/wiki/PreLoading-Lovelace-Elements
|
|
||||||
const pictureGlance = await this._helpers.createCardElement({
|
|
||||||
type: 'picture-glance',
|
|
||||||
entities: [],
|
|
||||||
camera_image: 'dummy-to-load-editor-components',
|
|
||||||
});
|
|
||||||
if (pictureGlance.constructor.getConfigElement) {
|
|
||||||
await pictureGlance.constructor.getConfigElement();
|
|
||||||
this._initialized = true;
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Load card helpers.
|
|
||||||
*/
|
|
||||||
protected async loadCardHelpers(): Promise<void> {
|
|
||||||
this._helpers = await (window as any).loadCardHelpers();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Close the editor menu with the given domain.
|
* Close the editor menu with the given domain.
|
||||||
* @param targetDomain The menu domain to close.
|
* @param targetDomain The menu domain to close.
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import {
|
|||||||
CustomActionConfig,
|
CustomActionConfig,
|
||||||
HomeAssistant,
|
HomeAssistant,
|
||||||
LovelaceCard,
|
LovelaceCard,
|
||||||
|
LovelaceCardConfig,
|
||||||
LovelaceCardEditor,
|
LovelaceCardEditor,
|
||||||
MoreInfoActionConfig,
|
MoreInfoActionConfig,
|
||||||
NavigateActionConfig,
|
NavigateActionConfig,
|
||||||
@@ -1152,6 +1153,14 @@ export interface FrigateCardMediaPlayer {
|
|||||||
unmute(): void;
|
unmute(): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface CardHelpers {
|
||||||
|
createCardElement(config: LovelaceCardConfig): Promise<{
|
||||||
|
constructor: {
|
||||||
|
getConfigElement(): HTMLElement
|
||||||
|
};
|
||||||
|
}>;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Home Assistant API types.
|
* Home Assistant API types.
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user