mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-24 09:46:36 +00:00
Improve warning messages in conditional card (#18272)
* Improve warning messages in conditional card * Update src/panels/lovelace/editor/conditions/ha-card-condition-editor.ts Co-authored-by: Bram Kragten <mail@bramkragten.nl> --------- Co-authored-by: Bram Kragten <mail@bramkragten.nl>
This commit is contained in:
parent
c48b620e03
commit
c6be4d6f4d
@ -13,45 +13,33 @@ export const handleStructError = (
|
||||
for (const failure of err.failures()) {
|
||||
if (failure.value === undefined) {
|
||||
errors.push(
|
||||
hass.localize(
|
||||
"ui.errors.config.key_missing",
|
||||
"key",
|
||||
failure.path.join(".")
|
||||
)
|
||||
hass.localize("ui.errors.config.key_missing", {
|
||||
key: failure.path.join("."),
|
||||
})
|
||||
);
|
||||
} else if (failure.type === "never") {
|
||||
warnings.push(
|
||||
hass.localize(
|
||||
"ui.errors.config.key_not_expected",
|
||||
"key",
|
||||
failure.path.join(".")
|
||||
)
|
||||
hass.localize("ui.errors.config.key_not_expected", {
|
||||
key: failure.path.join("."),
|
||||
})
|
||||
);
|
||||
} else if (failure.type === "union") {
|
||||
continue;
|
||||
} else if (failure.type === "enums") {
|
||||
warnings.push(
|
||||
hass.localize(
|
||||
"ui.errors.config.key_wrong_type",
|
||||
"key",
|
||||
failure.path.join("."),
|
||||
"type_correct",
|
||||
failure.message.replace("Expected ", "").split(", ")[0],
|
||||
"type_wrong",
|
||||
JSON.stringify(failure.value)
|
||||
)
|
||||
hass.localize("ui.errors.config.key_wrong_type", {
|
||||
key: failure.path.join("."),
|
||||
type_correct: failure.message.replace("Expected ", "").split(", ")[0],
|
||||
type_wrong: JSON.stringify(failure.value),
|
||||
})
|
||||
);
|
||||
} else {
|
||||
warnings.push(
|
||||
hass.localize(
|
||||
"ui.errors.config.key_wrong_type",
|
||||
"key",
|
||||
failure.path.join("."),
|
||||
"type_correct",
|
||||
failure.refinement || failure.type,
|
||||
"type_wrong",
|
||||
JSON.stringify(failure.value)
|
||||
)
|
||||
hass.localize("ui.errors.config.key_wrong_type", {
|
||||
key: failure.path.join("."),
|
||||
type_correct: failure.refinement || failure.type,
|
||||
type_wrong: JSON.stringify(failure.value),
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { Statistic, StatisticType } from "../../../data/recorder";
|
||||
import { ActionConfig, LovelaceCardConfig } from "../../../data/lovelace";
|
||||
import { FullCalendarView, TranslationDict } from "../../../types";
|
||||
import { Condition } from "../common/validate-condition";
|
||||
import { Condition, LegacyCondition } from "../common/validate-condition";
|
||||
import { HuiImage } from "../components/hui-image";
|
||||
import { LovelaceElementConfig } from "../elements/types";
|
||||
import {
|
||||
@ -37,7 +37,7 @@ export interface CalendarCardConfig extends LovelaceCardConfig {
|
||||
|
||||
export interface ConditionalCardConfig extends LovelaceCardConfig {
|
||||
card: LovelaceCardConfig;
|
||||
conditions: Condition[];
|
||||
conditions: (Condition | LegacyCondition)[];
|
||||
}
|
||||
|
||||
export interface EmptyStateCardConfig extends LovelaceCardConfig {
|
||||
|
@ -21,7 +21,10 @@ export type ScreenCondition = {
|
||||
media_query?: string;
|
||||
};
|
||||
|
||||
function checkStateCondition(condition: StateCondition, hass: HomeAssistant) {
|
||||
function checkStateCondition(
|
||||
condition: StateCondition | LegacyCondition,
|
||||
hass: HomeAssistant
|
||||
) {
|
||||
const state =
|
||||
condition.entity && hass.states[condition.entity]
|
||||
? hass.states[condition.entity].state
|
||||
@ -42,19 +45,20 @@ function checkScreenCondition(
|
||||
}
|
||||
|
||||
export function checkConditionsMet(
|
||||
conditions: Condition[],
|
||||
conditions: (Condition | LegacyCondition)[],
|
||||
hass: HomeAssistant
|
||||
): boolean {
|
||||
return conditions.every((c) => {
|
||||
if (c.condition === "screen") {
|
||||
return checkScreenCondition(c, hass);
|
||||
if ("condition" in c) {
|
||||
if (c.condition === "screen") {
|
||||
return checkScreenCondition(c, hass);
|
||||
}
|
||||
}
|
||||
|
||||
return checkStateCondition(c, hass);
|
||||
});
|
||||
}
|
||||
|
||||
function valideStateCondition(condition: StateCondition) {
|
||||
function valideStateCondition(condition: StateCondition | LegacyCondition) {
|
||||
return (
|
||||
condition.entity != null &&
|
||||
(condition.state != null || condition.state_not != null)
|
||||
@ -65,10 +69,14 @@ function validateScreenCondition(condition: ScreenCondition) {
|
||||
return condition.media_query != null;
|
||||
}
|
||||
|
||||
export function validateConditionalConfig(conditions: Condition[]): boolean {
|
||||
export function validateConditionalConfig(
|
||||
conditions: (Condition | LegacyCondition)[]
|
||||
): boolean {
|
||||
return conditions.every((c) => {
|
||||
if (c.condition === "screen") {
|
||||
return validateScreenCondition(c);
|
||||
if ("condition" in c) {
|
||||
if (c.condition === "screen") {
|
||||
return validateScreenCondition(c);
|
||||
}
|
||||
}
|
||||
return valideStateCondition(c);
|
||||
});
|
||||
|
@ -78,7 +78,7 @@ export class HuiConditionalBase extends ReactiveElement {
|
||||
}
|
||||
|
||||
const conditions = this._config.conditions.filter(
|
||||
(c) => c.condition === "screen"
|
||||
(c) => "condition" in c && c.condition === "screen"
|
||||
) as ScreenCondition[];
|
||||
|
||||
const mediaQueries = conditions
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { preventDefault } from "@fullcalendar/core/internal";
|
||||
import { ActionDetail } from "@material/mwc-list";
|
||||
import { mdiCheck, mdiDelete, mdiDotsVertical } from "@mdi/js";
|
||||
import { LitElement, css, html, nothing } from "lit";
|
||||
import { LitElement, PropertyValues, css, html, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { dynamicElement } from "../../../../common/dom/dynamic-element-directive";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
@ -13,33 +13,59 @@ import "../../../../components/ha-svg-icon";
|
||||
import "../../../../components/ha-yaml-editor";
|
||||
import { haStyle } from "../../../../resources/styles";
|
||||
import type { HomeAssistant } from "../../../../types";
|
||||
import { Condition, LegacyCondition } from "../../common/validate-condition";
|
||||
import type { LovelaceConditionEditorConstructor } from "./types";
|
||||
import { ICON_CONDITION } from "../../common/icon-condition";
|
||||
import { Condition } from "../../common/validate-condition";
|
||||
import type { LovelaceConditionEditorConstructor } from "./types";
|
||||
import { handleStructError } from "../../../../common/structs/handle-errors";
|
||||
|
||||
@customElement("ha-card-condition-editor")
|
||||
export default class HaCardConditionEditor extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) condition!: Condition | LegacyCondition;
|
||||
@property({ attribute: false }) condition!: Condition;
|
||||
|
||||
@state() public _yamlMode = false;
|
||||
|
||||
protected render() {
|
||||
const condition: Condition = {
|
||||
condition: "state",
|
||||
...this.condition,
|
||||
};
|
||||
@state() public _uiAvailable = false;
|
||||
|
||||
@state() public _uiWarnings: string[] = [];
|
||||
|
||||
private get _editor() {
|
||||
const element = customElements.get(
|
||||
`ha-card-condition-${condition.condition}`
|
||||
`ha-card-condition-${this.condition.condition}`
|
||||
) as LovelaceConditionEditorConstructor | undefined;
|
||||
const supported = element !== undefined;
|
||||
|
||||
const valid =
|
||||
element &&
|
||||
(!element.validateUIConfig || element.validateUIConfig(condition));
|
||||
return element;
|
||||
}
|
||||
|
||||
const yamlMode = this._yamlMode || !supported || !valid;
|
||||
protected willUpdate(changedProperties: PropertyValues): void {
|
||||
if (changedProperties.has("condition")) {
|
||||
const validator = this._editor?.validateUIConfig;
|
||||
if (validator) {
|
||||
try {
|
||||
validator(this.condition, this.hass);
|
||||
this._uiAvailable = true;
|
||||
this._uiWarnings = [];
|
||||
} catch (err) {
|
||||
this._uiWarnings = handleStructError(
|
||||
this.hass,
|
||||
err as Error
|
||||
).warnings;
|
||||
this._uiAvailable = false;
|
||||
}
|
||||
} else {
|
||||
this._uiAvailable = false;
|
||||
this._uiWarnings = [];
|
||||
}
|
||||
|
||||
if (!this._uiAvailable && !this._yamlMode) {
|
||||
this._yamlMode = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected render() {
|
||||
const condition = this.condition;
|
||||
|
||||
return html`
|
||||
<div class="header">
|
||||
@ -68,9 +94,9 @@ export default class HaCardConditionEditor extends LitElement {
|
||||
>
|
||||
</ha-icon-button>
|
||||
|
||||
<ha-list-item graphic="icon" .disabled=${!supported || !valid}>
|
||||
<ha-list-item graphic="icon" .disabled=${!this._uiAvailable}>
|
||||
${this.hass.localize("ui.panel.lovelace.editor.edit_card.edit_ui")}
|
||||
${!yamlMode
|
||||
${!this._yamlMode
|
||||
? html`
|
||||
<ha-svg-icon
|
||||
class="selected_menu_item"
|
||||
@ -85,7 +111,7 @@ export default class HaCardConditionEditor extends LitElement {
|
||||
${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.edit_card.edit_yaml"
|
||||
)}
|
||||
${yamlMode
|
||||
${this._yamlMode
|
||||
? html`
|
||||
<ha-svg-icon
|
||||
class="selected_menu_item"
|
||||
@ -108,15 +134,30 @@ export default class HaCardConditionEditor extends LitElement {
|
||||
</ha-list-item>
|
||||
</ha-button-menu>
|
||||
</div>
|
||||
${!valid
|
||||
${!this._uiAvailable
|
||||
? html`
|
||||
<ha-alert alert-type="warning">
|
||||
${this.hass.localize("ui.errors.config.editor_not_supported")}
|
||||
<ha-alert
|
||||
alert-type="warning"
|
||||
.title=${this.hass.localize(
|
||||
"ui.errors.config.editor_not_supported"
|
||||
)}
|
||||
>
|
||||
${this._uiWarnings!.length > 0 &&
|
||||
this._uiWarnings![0] !== undefined
|
||||
? html`
|
||||
<ul>
|
||||
${this._uiWarnings!.map(
|
||||
(warning) => html`<li>${warning}</li>`
|
||||
)}
|
||||
</ul>
|
||||
`
|
||||
: nothing}
|
||||
${this.hass.localize("ui.errors.config.edit_in_yaml_supported")}
|
||||
</ha-alert>
|
||||
`
|
||||
: nothing}
|
||||
<div class="content">
|
||||
${yamlMode
|
||||
${this._yamlMode
|
||||
? html`
|
||||
<ha-yaml-editor
|
||||
.hass=${this.hass}
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { HomeAssistant } from "../../../../types";
|
||||
import { Condition } from "../../common/validate-condition";
|
||||
|
||||
export interface LovelaceConditionEditorConstructor {
|
||||
defaultConfig?: Condition;
|
||||
validateUIConfig?: (condition: Condition) => boolean;
|
||||
validateUIConfig?: (condition: Condition, hass: HomeAssistant) => void;
|
||||
}
|
||||
|
@ -103,10 +103,17 @@ export class HaCardConditionScreen extends LitElement {
|
||||
return { condition: "screen", media_query: "" };
|
||||
}
|
||||
|
||||
protected static validateUIConfig(condition: ScreenCondition) {
|
||||
return (
|
||||
!condition.media_query || mediaQueryReverseMap.get(condition.media_query)
|
||||
);
|
||||
protected static validateUIConfig(
|
||||
condition: ScreenCondition,
|
||||
hass: HomeAssistant
|
||||
) {
|
||||
const valid =
|
||||
!condition.media_query || mediaQueryReverseMap.has(condition.media_query);
|
||||
if (!valid) {
|
||||
throw new Error(
|
||||
hass.localize("ui.errors.config.media_query_not_supported")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private _schema = memoizeOne(
|
||||
|
@ -12,7 +12,7 @@ import { StateCondition } from "../../../common/validate-condition";
|
||||
|
||||
const stateConditionStruct = object({
|
||||
condition: literal("state"),
|
||||
entity: string(),
|
||||
entity: optional(string()),
|
||||
state: optional(string()),
|
||||
state_not: optional(string()),
|
||||
});
|
||||
@ -36,6 +36,10 @@ export class HaCardConditionState extends LitElement {
|
||||
return { condition: "state", entity: "", state: "" };
|
||||
}
|
||||
|
||||
protected static validateUIConfig(condition: StateCondition) {
|
||||
return assert(condition, stateConditionStruct);
|
||||
}
|
||||
|
||||
protected willUpdate(changedProperties: PropertyValues): void {
|
||||
if (!changedProperties.has("condition")) {
|
||||
return;
|
||||
|
@ -163,21 +163,27 @@ export class HuiConditionalCardEditor
|
||||
`
|
||||
: html`
|
||||
<div class="conditions">
|
||||
${this.hass!.localize(
|
||||
"ui.panel.lovelace.editor.card.conditional.condition_explanation"
|
||||
)}
|
||||
${this._config.conditions.map(
|
||||
(cond, idx) => html`
|
||||
<ha-alert alert-type="info">
|
||||
${this.hass!.localize(
|
||||
"ui.panel.lovelace.editor.card.conditional.condition_explanation"
|
||||
)}
|
||||
</ha-alert>
|
||||
${this._config.conditions.map((cond, idx) => {
|
||||
const condition: Condition = {
|
||||
condition: "state",
|
||||
...cond,
|
||||
};
|
||||
return html`
|
||||
<div class="condition">
|
||||
<ha-card-condition-editor
|
||||
.index=${idx}
|
||||
@value-changed=${this._conditionChanged}
|
||||
.hass=${this.hass}
|
||||
.condition=${cond}
|
||||
.condition=${condition}
|
||||
></ha-card-condition-editor>
|
||||
</div>
|
||||
`
|
||||
)}
|
||||
`;
|
||||
})}
|
||||
<div>
|
||||
<ha-button-menu
|
||||
@action=${this._addCondition}
|
||||
|
@ -1488,7 +1488,8 @@
|
||||
"key_missing": "Required key ''{key}'' is missing.",
|
||||
"key_not_expected": "Key ''{key}'' is not expected or not supported by the visual editor.",
|
||||
"key_wrong_type": "The provided value for ''{key}'' is not supported by the visual editor. We support ({type_correct}) but received ({type_wrong}).",
|
||||
"no_template_editor_support": "Templates not supported in visual editor"
|
||||
"no_template_editor_support": "Templates not supported in visual editor",
|
||||
"media_query_not_supported": "This media query is not supported by the visual editor."
|
||||
},
|
||||
"supervisor": {
|
||||
"title": "Could not load the Supervisor panel!",
|
||||
|
Loading…
x
Reference in New Issue
Block a user