Add UI for responsive condition

This commit is contained in:
Paul Bottein 2023-09-27 13:11:59 +02:00
parent 8bcd730501
commit c6e6255d19
No known key found for this signature in database
6 changed files with 185 additions and 77 deletions

View File

@ -8,6 +8,7 @@ import "../../../components/ha-yaml-editor";
import { haStyle } from "../../../resources/styles";
import type { HomeAssistant } from "../../../types";
import "./types/ha-card-condition-state";
import "./types/ha-card-condition-responsive";
import { Condition } from "./validate-condition";
@customElement("ha-card-condition-editor")

View File

@ -0,0 +1,107 @@
import { html, LitElement, PropertyValues } from "lit";
import { customElement, property } from "lit/decorators";
import { assert, literal, number, object, optional } from "superstruct";
import { fireEvent } from "../../../../common/dom/fire_event";
import "../../../../components/ha-form/ha-form";
import type { SchemaUnion } from "../../../../components/ha-form/types";
import { HaFormSchema } from "../../../../components/ha-form/types";
import type { HomeAssistant } from "../../../../types";
import { ResponsiveCondition } from "../validate-condition";
const responsiveConditionStruct = object({
condition: literal("responsive"),
max_width: optional(number()),
min_width: optional(number()),
});
const SCHEMA = [
{
name: "",
type: "grid",
schema: [
{
name: "min_width",
selector: {
number: {
mode: "box",
step: 1,
unit_of_measurement: "px",
},
},
},
{
name: "max_width",
selector: {
number: {
mode: "box",
step: 1,
unit_of_measurement: "px",
},
},
},
],
},
] as const satisfies readonly HaFormSchema[];
@customElement("ha-card-condition-responsive")
export class HaCardConditionResponsive extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public condition!: ResponsiveCondition;
@property({ type: Boolean }) public disabled = false;
public static get defaultConfig(): ResponsiveCondition {
return { condition: "responsive" };
}
protected willUpdate(changedProperties: PropertyValues): void {
if (!changedProperties.has("condition")) {
return;
}
try {
assert(this.condition, responsiveConditionStruct);
} catch (err: any) {
fireEvent(this, "ui-mode-not-available", err);
}
}
protected render() {
return html`
<ha-form
.hass=${this.hass}
.data=${this.condition}
.schema=${SCHEMA}
.disabled=${this.disabled}
@value-changed=${this._valueChanged}
.computeLabel=${this._computeLabelCallback}
></ha-form>
`;
}
private _valueChanged(ev: CustomEvent): void {
ev.stopPropagation();
const data = ev.detail.value as ResponsiveCondition;
fireEvent(this, "value-changed", { value: data });
}
private _computeLabelCallback = (
schema: SchemaUnion<typeof SCHEMA>
): string => {
switch (schema.name) {
case "min_width":
case "max_width":
return this.hass.localize(
`ui.panel.lovelace.editor.card.conditional.${schema.name}`
);
default:
return "";
}
};
}
declare global {
interface HTMLElementTagNameMap {
"ha-card-condition-responsive": HaCardConditionResponsive;
}
}

View File

@ -1,13 +1,22 @@
import type { HassEntity } from "home-assistant-js-websocket";
import { html, LitElement } from "lit";
import { html, LitElement, PropertyValues } from "lit";
import { customElement, property } from "lit/decorators";
import memoizeOne from "memoize-one";
import { assert, literal, object, optional, string } from "superstruct";
import { fireEvent } from "../../../../common/dom/fire_event";
import { LocalizeFunc } from "../../../../common/translations/localize";
import "../../../../components/ha-form/ha-form";
import type { SchemaUnion } from "../../../../components/ha-form/types";
import { HaFormSchema } from "../../../../components/ha-form/types";
import type { HomeAssistant } from "../../../../types";
import { StateCondition } from "../validate-condition";
const stateConditionStruct = object({
condition: literal("state"),
entity: string(),
state: optional(string()),
state_not: optional(string()),
});
type StateConditionData = {
condition: "state";
entity: string;
@ -15,7 +24,32 @@ type StateConditionData = {
state?: string;
};
const SCHEMA = [
@customElement("ha-card-condition-state")
export class HaCardConditionState extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public condition!: StateCondition;
@property({ type: Boolean }) public disabled = false;
public static get defaultConfig(): StateCondition {
return { condition: "state", entity: "" };
}
protected willUpdate(changedProperties: PropertyValues): void {
if (!changedProperties.has("condition")) {
return;
}
try {
assert(this.condition, stateConditionStruct);
} catch (err: any) {
fireEvent(this, "ui-mode-not-available", err);
}
}
private _schema = memoizeOne(
(localize: LocalizeFunc) =>
[
{ name: "entity", selector: { entity: {} } },
{
name: "",
@ -28,11 +62,15 @@ const SCHEMA = [
mode: "dropdown",
options: [
{
label: "State equal",
label: localize(
"ui.panel.lovelace.editor.card.conditional.state_equal"
),
value: "false",
},
{
label: "State not equal",
label: localize(
"ui.panel.lovelace.editor.card.conditional.state_not_equal"
),
value: "true",
},
],
@ -50,19 +88,8 @@ const SCHEMA = [
},
],
},
] as const satisfies readonly HaFormSchema[];
@customElement("ha-card-condition-state")
export class HaCardConditionState extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public condition!: StateCondition;
@property({ type: Boolean }) public disabled = false;
public static get defaultConfig(): StateCondition {
return { condition: "state", entity: "", state: "" };
}
] as const satisfies readonly HaFormSchema[]
);
protected render() {
const { state, state_not, ...content } = this.condition;
@ -77,7 +104,7 @@ export class HaCardConditionState extends LitElement {
<ha-form
.hass=${this.hass}
.data=${data}
.schema=${SCHEMA}
.schema=${this._schema(this.hass.localize)}
.disabled=${this.disabled}
@value-changed=${this._valueChanged}
.computeLabel=${this._computeLabelCallback}
@ -103,11 +130,11 @@ export class HaCardConditionState extends LitElement {
}
private _computeLabelCallback = (
schema: SchemaUnion<typeof SCHEMA>
schema: SchemaUnion<ReturnType<typeof this._schema>>
): string => {
const entity = this.hass.states[this.condition.entity] as
| HassEntity
| undefined;
const entity = this.condition.entity
? this.hass.states[this.condition.entity]
: undefined;
switch (schema.name) {
case "entity":
return this.hass.localize("ui.components.entity.entity-picker.entity");

View File

@ -18,7 +18,7 @@ export type ResponsiveCondition = {
function checkStateCondition(condition: StateCondition, hass: HomeAssistant) {
const state = hass.states[condition.entity]
? hass!.states[condition.entity].state
? hass.states[condition.entity].state
: UNAVAILABLE;
return condition.state != null

View File

@ -1,4 +1,3 @@
import "@material/mwc-list/mwc-list-item";
import "@material/mwc-tab-bar/mwc-tab-bar";
import "@material/mwc-tab/mwc-tab";
import type { MDCTabBarActivatedEvent } from "@material/tab-bar";
@ -6,23 +5,10 @@ import { mdiCodeBraces, mdiContentCopy, mdiListBoxOutline } from "@mdi/js";
import deepClone from "deep-clone-simple";
import { CSSResultGroup, LitElement, css, html, nothing } from "lit";
import { customElement, property, query, state } from "lit/decorators";
import {
any,
array,
assert,
assign,
literal,
number,
object,
optional,
string,
union,
} from "superstruct";
import { any, array, assert, assign, object, optional } from "superstruct";
import { storage } from "../../../../common/decorators/storage";
import { HASSDomEvent, fireEvent } from "../../../../common/dom/fire_event";
import "../../../../components/entity/ha-entity-picker";
import "../../../../components/ha-select";
import "../../../../components/ha-textfield";
import type {
LovelaceCardConfig,
LovelaceConfig,
@ -40,26 +26,11 @@ import { baseLovelaceCardConfig } from "../structs/base-card-struct";
import type { GUIModeChangedEvent } from "../types";
import { configElementStyle } from "./config-elements-style";
const stateConditionStruct = object({
condition: optional(literal("state")),
entity: string(),
state: optional(string()),
state_not: optional(string()),
});
const responsiveConditionStruct = object({
condition: literal("responsive"),
max_width: optional(number()),
min_width: optional(number()),
});
const cardConfigStruct = assign(
baseLovelaceCardConfig,
object({
card: any(),
conditions: optional(
array(union([stateConditionStruct, responsiveConditionStruct]))
),
conditions: optional(array(any())),
})
);

View File

@ -4767,6 +4767,8 @@
"state_equal": "State is equal to",
"state_not_equal": "State is not equal to",
"current_state": "current",
"min_width": "Min width",
"max_width": "Max width",
"condition_explanation": "The card will be shown when ALL conditions below are fulfilled.",
"change_type": "Change type"
},