import { css, html, LitElement, type TemplateResult } from 'lit'; import { SIDE_LOADED_ELEMENTS } from '../../src/ha/side-load-ha-elements'; /** * The base the three `src/patches` classes subclass at runtime. Home * Assistant's real players own a `` and a transport; this carries only * the members the patches touch, so the subclasses can be defined and * constructed. It plays nothing. */ class HAPlayerStandIn extends LitElement { static styles = css` :host { display: block; } `; public entityid?: string; public posterUrl?: string; public autoPlay = false; public muted = true; public playsInline = true; public controls = false; protected _error?: string; protected _errorIsFatal = false; protected _loadedData(): void { // The patches call through to this on the 's `loadeddata`. } protected async _startWebRtc(): Promise { // WebRTC negotiation, which this stand-in does not perform. } protected _cleanUp(): void { // Transport teardown, which this stand-in has none of. } } /** * A stand-in that renders its children and nothing else. Behaviour is added * only for elements the card is observed to depend on. */ class HAElementStub extends LitElement { static styles = css` :host { display: block; } `; protected render(): TemplateResult { return html``; } } /** * The surface a card is drawn on. Home Assistant's own `:host` styles to ensure * the card has background, border and corners for screenshots. */ class HACardStub extends HAElementStub { static styles = css` :host { background: var(--ha-card-background, var(--card-background-color, white)); box-shadow: var(--ha-card-box-shadow, none); box-sizing: border-box; border-radius: var(--ha-card-border-radius, var(--ha-border-radius-lg)); border-width: var(--ha-card-border-width, 1px); border-style: solid; border-color: var(--ha-card-border-color, var(--divider-color, #e0e0e0)); color: var(--primary-text-color); display: block; position: relative; } `; } /** * A round tap target sized by `--ha-icon-button-size`, which is how Home * Assistant's own icon button sizes itself and what the card sets to lay its * menu out. Without it a button is only as big as whatever it contains. */ class HAIconButtonStub extends HAElementStub { static styles = css` :host { display: inline-flex; align-items: center; justify-content: center; width: var(--ha-icon-button-size, 48px); height: var(--ha-icon-button-size, 48px); border-radius: 50%; box-sizing: border-box; outline: none; } `; } /** * Home Assistant fetches `mdi:` icons from Home Assistant, so there is nothing * to draw here. It still occupies an icon's space, so that the layout around it * is the layout a user would see. */ class HAIconStub extends LitElement { static styles = css` :host { display: inline-block; width: var(--mdc-icon-size, 24px); height: var(--mdc-icon-size, 24px); } `; } /** * Home Assistant's conditional picture element, which the card builds one of on * every mount to host whatever picture elements are configured. */ class HuiConditionalElementStub extends HTMLElement { public hass?: unknown; public setConfig(): void { // The card only needs the call to succeed; nothing renders from it here. } } // A fresh anonymous subclass every call: `customElements.define` rejects a // constructor that is already registered under another name. const createStub = (element: string): CustomElementConstructor => { switch (element) { case 'ha-camera-stream': case 'ha-hls-player': case 'ha-web-rtc-player': return class extends HAPlayerStandIn {}; case 'ha-card': return class extends HACardStub {}; case 'ha-icon-button': case 'ha-icon-button-prev': return class extends HAIconButtonStub {}; case 'ha-icon': case 'ha-state-icon': case 'state-badge': return class extends HAIconStub {}; case 'hui-conditional-element': return class extends HuiConditionalElementStub {}; default: return class extends HAElementStub {}; } }; const defineElement = (name: string, constructor: CustomElementConstructor): void => { if (!customElements.get(name)) { customElements.define(name, constructor); } }; /** * Define every Home Assistant element the card expects to already exist, so * that sideLoadHomeAssistantElements() returns at its first check and never * reaches its `picture-glance` side-load trick. */ export const defineHAElementStubs = (): void => { for (const element of SIDE_LOADED_ELEMENTS) { defineElement(element, createStub(element)); } };