Add live camera & custom AR to Area card (#18643)

This commit is contained in:
karwosts 2023-11-18 08:26:21 -08:00 committed by GitHub
parent a1236924aa
commit a45eefa742
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 94 additions and 28 deletions

View File

@ -21,6 +21,7 @@ import {
} from "lit";
import { customElement, property, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import { styleMap } from "lit/directives/style-map";
import memoizeOne from "memoize-one";
import { STATES_OFF } from "../../../common/const";
import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element";
@ -29,6 +30,7 @@ import { domainIcon } from "../../../common/entity/domain_icon";
import { navigate } from "../../../common/navigate";
import { formatNumber } from "../../../common/number/format_number";
import { subscribeOne } from "../../../common/util/subscribe-one";
import parseAspectRatio from "../../../common/util/parse-aspect-ratio";
import "../../../components/entity/state-badge";
import "../../../components/ha-card";
import "../../../components/ha-icon-button";
@ -55,6 +57,8 @@ import "../components/hui-warning";
import { LovelaceCard, LovelaceCardEditor } from "../types";
import { AreaCardConfig } from "./types";
export const DEFAULT_ASPECT_RATIO = "16:9";
const SENSOR_DOMAINS = ["sensor"];
const ALERT_DOMAINS = ["binary_sensor"];
@ -109,6 +113,11 @@ export class HuiAreaCard
@state() private _areas?: AreaRegistryEntry[];
private _ratio: {
w: number;
h: number;
} | null = null;
private _entitiesByDomain = memoizeOne(
(
areaId: string,
@ -319,6 +328,18 @@ export class HuiAreaCard
return false;
}
public willUpdate(changedProps: PropertyValues) {
if (changedProps.has("_config") || this._ratio === null) {
this._ratio = this._config?.aspect_ratio
? parseAspectRatio(this._config?.aspect_ratio)
: null;
if (this._ratio === null || this._ratio.w <= 0 || this._ratio.h <= 0) {
this._ratio = parseAspectRatio(DEFAULT_ASPECT_RATIO);
}
}
}
protected render() {
if (
!this._config ||
@ -374,15 +395,24 @@ export class HuiAreaCard
cameraEntityId = entitiesByDomain.camera[0].entity_id;
}
const imageClass = area.picture || cameraEntityId;
return html`
<ha-card class=${area.picture || cameraEntityId ? "image" : ""}>
<ha-card
class=${imageClass ? "image" : ""}
style=${styleMap({
paddingBottom: imageClass
? "0"
: `${((100 * this._ratio!.h) / this._ratio!.w).toFixed(2)}%`,
})}
>
${area.picture || cameraEntityId
? html`<hui-image
.config=${this._config}
.hass=${this.hass}
.image=${area.picture ? area.picture : undefined}
.cameraImage=${cameraEntityId}
aspectRatio="16:9"
.cameraView=${this._config.camera_view}
.aspectRatio=${this._config.aspect_ratio || DEFAULT_ASPECT_RATIO}
></hui-image>`
: ""}
@ -488,14 +518,9 @@ export class HuiAreaCard
ha-card {
overflow: hidden;
position: relative;
padding-bottom: 56.25%;
background-size: cover;
}
ha-card.image {
padding-bottom: 0;
}
.container {
display: flex;
flex-direction: column;

View File

@ -93,6 +93,8 @@ export interface AreaCardConfig extends LovelaceCardConfig {
area: string;
navigation_path?: string;
show_camera?: boolean;
camera_view?: HuiImage["cameraView"];
aspect_ratio?: string;
}
export interface ButtonCardConfig extends LovelaceCardConfig {

View File

@ -1,8 +1,10 @@
import { html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import { assert, assign, boolean, object, optional, string } from "superstruct";
import { fireEvent } from "../../../../common/dom/fire_event";
import "../../../../components/ha-form/ha-form";
import { DEFAULT_ASPECT_RATIO } from "../../cards/hui-area-card";
import type { SchemaUnion } from "../../../../components/ha-form/types";
import type { HomeAssistant } from "../../../../types";
import type { AreaCardConfig } from "../../cards/types";
@ -16,26 +18,10 @@ const cardConfigStruct = assign(
navigation_path: optional(string()),
theme: optional(string()),
show_camera: optional(boolean()),
camera_view: optional(string()),
aspect_ratio: optional(string()),
})
);
const SCHEMA = [
{ name: "area", selector: { area: {} } },
{ name: "show_camera", required: false, selector: { boolean: {} } },
{
name: "",
type: "grid",
schema: [
{
name: "navigation_path",
required: false,
selector: { navigation: {} },
},
{ name: "theme", required: false, selector: { theme: {} } },
],
},
] as const;
@customElement("hui-area-card-editor")
export class HuiAreaCardEditor
extends LitElement
@ -45,6 +31,39 @@ export class HuiAreaCardEditor
@state() private _config?: AreaCardConfig;
private _schema = memoizeOne(
(showCamera: boolean) =>
[
{ name: "area", selector: { area: {} } },
{ name: "show_camera", required: false, selector: { boolean: {} } },
...(showCamera
? ([
{
name: "camera_view",
selector: { select: { options: ["auto", "live"] } },
},
] as const)
: []),
{
name: "",
type: "grid",
schema: [
{
name: "navigation_path",
required: false,
selector: { navigation: {} },
},
{ name: "theme", required: false, selector: { theme: {} } },
{
name: "aspect_ratio",
default: DEFAULT_ASPECT_RATIO,
selector: { text: {} },
},
],
},
] as const
);
public setConfig(config: AreaCardConfig): void {
assert(config, cardConfigStruct);
this._config = config;
@ -55,11 +74,18 @@ export class HuiAreaCardEditor
return nothing;
}
const schema = this._schema(this._config.show_camera || false);
const data = {
camera_view: "auto",
...this._config,
};
return html`
<ha-form
.hass=${this.hass}
.data=${this._config}
.schema=${SCHEMA}
.data=${data}
.schema=${schema}
.computeLabel=${this._computeLabelCallback}
@value-changed=${this._valueChanged}
></ha-form>
@ -68,10 +94,15 @@ export class HuiAreaCardEditor
private _valueChanged(ev: CustomEvent): void {
const config = ev.detail.value;
if (!config.show_camera) {
delete config.camera_view;
}
fireEvent(this, "config-changed", { config });
}
private _computeLabelCallback = (schema: SchemaUnion<typeof SCHEMA>) => {
private _computeLabelCallback = (
schema: SchemaUnion<ReturnType<typeof this._schema>>
) => {
switch (schema.name) {
case "theme":
return `${this.hass!.localize(
@ -85,6 +116,14 @@ export class HuiAreaCardEditor
return this.hass!.localize(
"ui.panel.lovelace.editor.action-editor.navigation_path"
);
case "aspect_ratio":
return this.hass!.localize(
"ui.panel.lovelace.editor.card.generic.aspect_ratio"
);
case "camera_view":
return this.hass!.localize(
"ui.panel.lovelace.editor.card.generic.camera_view"
);
}
return this.hass!.localize(
`ui.panel.lovelace.editor.card.area.${schema.name}`