Compare commits

...
6 changed files with 208 additions and 20 deletions
@@ -1,8 +1,8 @@
import memoizeOne from "memoize-one";
import { mdiChartBellCurveCumulative } from "@mdi/js";
import type { PropertyValues } from "lit";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { mdiChartBellCurveCumulative } from "@mdi/js";
import memoizeOne from "memoize-one";
import { fireEvent } from "../../common/dom/fire_event";
import type {
NumericThresholdSelector,
@@ -28,14 +28,14 @@ const iconThresholdOutside =
type ThresholdType = "above" | "below" | "between" | "outside" | "any";
interface ThresholdValueEntry {
export interface ThresholdValueEntry {
active_choice?: string;
number?: number;
entity?: string;
unit_of_measurement?: string;
}
interface NumericThresholdValue {
export interface NumericThresholdValue {
type: ThresholdType;
value?: ThresholdValueEntry;
value_min?: ThresholdValueEntry;
@@ -65,6 +65,7 @@ import {
import { fullEntitiesContext } from "../../../../data/context";
import type { EntityRegistryEntry } from "../../../../data/entity/entity_registry";
import type { TargetSelector } from "../../../../data/selector";
import { getTargetEntityCount } from "../../../../data/target";
import {
showAlertDialog,
showPromptDialog,
@@ -75,6 +76,7 @@ import { showEditorToast } from "../editor-toast";
import "../ha-automation-editor-warning";
import "../ha-automation-row-behavior";
import "../ha-automation-row-options";
import "../ha-automation-row-threshold";
import { overflowStyles, rowStyles } from "../styles";
import { getDeviceTarget } from "../target/get_device_target";
import { getEntityTarget } from "../target/get_entity_target";
@@ -232,10 +234,10 @@ export default class HaAutomationConditionRow extends LitElement {
)}
${
this._getType(this.condition, this.conditionDescriptions) ===
"platform"
"platform" && targetRequired
? html`<ha-automation-row-behavior
mode="condition"
.config=${this.condition}
.config=${getTargetEntityCount(target) > 1 ? this.condition : undefined}
></ha-automation-row-behavior>`
: nothing
}
@@ -249,6 +251,17 @@ export default class HaAutomationConditionRow extends LitElement {
)
: nothing
}
${
this._getType(this.condition, this.conditionDescriptions) ===
"platform"
? html`<ha-automation-row-threshold
.config=${this.condition}
.description=${
this.conditionDescriptions[this.condition.condition]
}
></ha-automation-row-threshold>`
: nothing
}
${
this._getType(this.condition, this.conditionDescriptions) ===
"platform"
@@ -1,5 +1,5 @@
import { consume, type ContextType } from "@lit/context";
import { html, LitElement, nothing, type PropertyValues } from "lit";
import { html, LitElement, type PropertyValues } from "lit";
import { customElement, property, state } from "lit/decorators";
import { internationalizationContext } from "../../../data/context";
import type {
@@ -16,7 +16,7 @@ interface HaAutomationRowBehaviorConfig {
@customElement("ha-automation-row-behavior")
export class HaAutomationRowBehavior extends LitElement {
@property({ attribute: false }) public config!: HaAutomationRowBehaviorConfig;
@property({ attribute: false }) public config?: HaAutomationRowBehaviorConfig;
@property() public mode: "trigger" | "condition" = "trigger";
@@ -40,22 +40,18 @@ export class HaAutomationRowBehavior extends LitElement {
);
}
protected render() {
const label = this._label;
if (!label) {
return nothing;
}
return html`<span class="dot-separator"></span>${label}`;
}
protected updated(changedProperties: PropertyValues): void {
super.updated(changedProperties);
// Collapse the host when empty so the parent flex gap is not reserved.
this.toggleAttribute("hidden", !this._label);
}
protected render() {
const label = this._label;
return html`<span class="dot-separator"></span>${label}`;
}
static styles = rowSummaryStyles;
}
@@ -0,0 +1,159 @@
import { consume, type ContextType } from "@lit/context";
import { html, LitElement, nothing, type PropertyValues } from "lit";
import { customElement, property, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import { formatNumber } from "../../../common/number/format_number";
import { blankBeforeUnit } from "../../../common/translations/blank_before_unit";
import type {
NumericThresholdValue,
ThresholdValueEntry,
} from "../../../components/ha-selector/ha-selector-numeric-threshold";
import type {
PlatformCondition,
PlatformTrigger,
} from "../../../data/automation";
import type { ConditionDescription } from "../../../data/condition";
import { internationalizationContext } from "../../../data/context";
import type { NumericThresholdSelector } from "../../../data/selector";
import type { TriggerDescription } from "../../../data/trigger";
import { rowSummaryStyles } from "./styles";
@customElement("ha-automation-row-threshold")
export class HaAutomationRowThreshold extends LitElement {
@property({ attribute: false })
public config!: PlatformTrigger | PlatformCondition;
@property({ attribute: false })
public description?: TriggerDescription | ConditionDescription;
@state()
@consume({ context: internationalizationContext, subscribe: true })
private _i18n!: ContextType<typeof internationalizationContext>;
protected updated(changedProperties: PropertyValues): void {
super.updated(changedProperties);
// Collapse the host when empty so the parent flex gap is not reserved.
this.toggleAttribute(
"hidden",
!this._getLabel(
this._getThreshold(this.config, this.description),
this._i18n
)
);
}
protected render() {
const label = this._getLabel(
this._getThreshold(this.config, this.description),
this._i18n
);
if (!label) {
return nothing;
}
return html`<span class="dot-separator"></span>${label}`;
}
private _getThreshold = memoizeOne(
(
config: PlatformTrigger | PlatformCondition,
description: TriggerDescription | ConditionDescription | undefined
) => {
const fields = description?.fields;
if (!fields || !config.options) {
return undefined;
}
const entry = Object.entries(fields).find(
([, field]) => field.selector && "numeric_threshold" in field.selector
);
if (!entry) {
return undefined;
}
const value = config.options[entry[0]] as
NumericThresholdValue | undefined;
if (!value?.type) {
return undefined;
}
const threshold = (entry[1].selector as NumericThresholdSelector)
.numeric_threshold;
const fallbackUnit =
threshold?.unit_of_measurement?.[0] ??
threshold?.number?.unit_of_measurement;
const withUnit = (bound?: ThresholdValueEntry) =>
bound
? {
...bound,
unit_of_measurement: bound.unit_of_measurement ?? fallbackUnit,
}
: bound;
return {
...value,
value: withUnit(value.value),
value_min: withUnit(value.value_min),
value_max: withUnit(value.value_max),
};
}
);
private _getLabel = memoizeOne(
(threshold: NumericThresholdValue | undefined, i18n: typeof this._i18n) => {
if (
!threshold ||
!threshold?.type ||
threshold.type === "any" ||
threshold.value?.active_choice === "entity" ||
threshold.value_min?.active_choice === "entity" ||
threshold.value_max?.active_choice === "entity"
) {
return undefined;
}
if (threshold.type === "between" || threshold.type === "outside") {
const min = threshold.value_min?.number;
const max = threshold.value_max?.number;
if (min === undefined || max === undefined) {
return undefined;
}
const separateUnits =
threshold.value_min?.unit_of_measurement !==
threshold.value_max?.unit_of_measurement;
const range = `${formatNumber(min, this._i18n.locale)}${separateUnits ? this._unit(threshold.value_min?.unit_of_measurement) : ""}-${formatNumber(max, this._i18n.locale)}${this._unit(threshold.value_max?.unit_of_measurement)}`;
return i18n.localize(
`ui.components.selectors.numeric_threshold.row_label.${threshold.type}`,
{ value: range }
);
}
if (isNaN(Number(threshold.value?.number))) {
return undefined;
}
return i18n.localize(
`ui.components.selectors.numeric_threshold.row_label.${threshold.type}`,
{
value: `${formatNumber(threshold.value!.number!, this._i18n.locale)}${this._unit(threshold.value!.unit_of_measurement ?? "")}`,
}
);
}
);
private _unit(unit?: string): string {
if (!unit) {
return "";
}
return `${blankBeforeUnit(unit, this._i18n.locale)}${unit}`;
}
static styles = rowSummaryStyles;
}
declare global {
interface HTMLElementTagNameMap {
"ha-automation-row-threshold": HaAutomationRowThreshold;
}
}
@@ -62,6 +62,7 @@ import { validateConfig } from "../../../../data/config";
import { fullEntitiesContext } from "../../../../data/context";
import type { EntityRegistryEntry } from "../../../../data/entity/entity_registry";
import type { TargetSelector } from "../../../../data/selector";
import { getTargetEntityCount } from "../../../../data/target";
import type { TriggerDescriptions } from "../../../../data/trigger";
import { isTriggerList } from "../../../../data/trigger";
import {
@@ -74,6 +75,7 @@ import { showEditorToast } from "../editor-toast";
import "../ha-automation-editor-warning";
import "../ha-automation-row-behavior";
import "../ha-automation-row-options";
import "../ha-automation-row-threshold";
import { overflowStyles, rowStyles } from "../styles";
import { getDeviceTarget } from "../target/get_device_target";
import { getEntityTarget } from "../target/get_entity_target";
@@ -256,9 +258,9 @@ export default class HaAutomationTriggerRow extends LitElement {
describeTrigger(this.trigger, this.hass, this._entityReg)
)}
${
type === "platform"
type === "platform" && targetRequired
? html`<ha-automation-row-behavior
.config=${this.trigger}
.config=${getTargetEntityCount(target) > 1 ? this.trigger : undefined}
></ha-automation-row-behavior>`
: nothing
}
@@ -272,6 +274,18 @@ export default class HaAutomationTriggerRow extends LitElement {
)
: nothing
}
${
type === "platform"
? html`<ha-automation-row-threshold
.config=${this.trigger}
.description=${
this.triggerDescriptions[
(this.trigger as PlatformTrigger).trigger
]
}
></ha-automation-row-threshold>`
: nothing
}
${
type === "platform"
? html`<ha-automation-row-options
+6
View File
@@ -637,6 +637,12 @@
"entity": "Entity",
"from": "From",
"to": "To",
"row_label": {
"above": "above {value}",
"below": "below {value}",
"between": "in range {value}",
"outside": "outside {value}"
},
"crossed": {
"type": "Fire when the value goes",
"above": "Above",