mirror of
https://github.com/home-assistant/frontend.git
synced 2026-04-29 22:13:23 +00:00
Compare commits
1 Commits
devtools-e
...
devtools-a
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
79aa3b4940 |
@@ -1,17 +0,0 @@
|
||||
<svg width="268" height="28" viewBox="0 0 268 28" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="88" height="28" rx="8" fill="white"/>
|
||||
<rect x="0.5" y="0.5" width="87" height="27" rx="7.5" stroke="black" stroke-opacity="0.12"/>
|
||||
<rect x="8" y="8" width="12" height="12" rx="3" fill="black" fill-opacity="0.12"/>
|
||||
<rect x="28" y="8" width="12" height="12" rx="3" fill="#009AC7"/>
|
||||
<rect x="48" y="8" width="12" height="12" rx="3" fill="black" fill-opacity="0.12"/>
|
||||
<rect x="68" y="8" width="12" height="12" rx="3" fill="black" fill-opacity="0.12"/>
|
||||
<path d="M107.667 18.1167V14.7833H100.233L100.208 13.1083H107.667V9.78333L111.833 13.95L107.667 18.1167Z" fill="#B1B1B1"/>
|
||||
<rect x="124" width="88" height="28" rx="8" fill="white"/>
|
||||
<rect x="124.5" y="0.5" width="87" height="27" rx="7.5" stroke="black" stroke-opacity="0.12"/>
|
||||
<rect x="132" y="8" width="12" height="12" rx="3" fill="black" fill-opacity="0.12"/>
|
||||
<rect x="152" y="8" width="12" height="12" rx="3" fill="#009AC7"/>
|
||||
<rect x="172" y="8" width="12" height="12" rx="3" fill="black" fill-opacity="0.12"/>
|
||||
<rect x="192" y="8" width="12" height="12" rx="3" fill="#009AC7"/>
|
||||
<path d="M237.5 13.1667H222.5V11.5H237.5V13.1667ZM237.5 14.8333H222.5V16.5H237.5V14.8333Z" fill="#B1B1B1"/>
|
||||
<path d="M257.167 16.5H253L258.833 4.83337V11.5H263L257.167 23.1667V16.5Z" fill="#5E5E5E"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.3 KiB |
@@ -1,17 +0,0 @@
|
||||
<svg width="268" height="28" viewBox="0 0 268 28" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M0 8C0 3.58172 3.58172 0 8 0H80C84.4183 0 88 3.58172 88 8V20C88 24.4183 84.4183 28 80 28H8C3.58172 28 0 24.4183 0 20V8Z" fill="#1C1C1C"/>
|
||||
<path d="M8 0.5H80C84.1421 0.5 87.5 3.85786 87.5 8V20C87.5 24.1421 84.1421 27.5 80 27.5H8C3.85786 27.5 0.5 24.1421 0.5 20V8C0.5 3.85786 3.85786 0.5 8 0.5Z" stroke="white" stroke-opacity="0.24"/>
|
||||
<rect x="8" y="8" width="12" height="12" rx="3" fill="white" fill-opacity="0.24"/>
|
||||
<rect x="28" y="8" width="12" height="12" rx="3" fill="#009AC7"/>
|
||||
<rect x="48" y="8" width="12" height="12" rx="3" fill="white" fill-opacity="0.24"/>
|
||||
<rect x="68" y="8" width="12" height="12" rx="3" fill="white" fill-opacity="0.24"/>
|
||||
<path d="M109.333 16.45C108.718 17.065 107.667 16.6294 107.667 15.7596C107.667 15.2204 107.23 14.7833 106.69 14.7833H101.058C100.601 14.7833 100.228 14.4159 100.221 13.9583C100.214 13.4909 100.591 13.1083 101.058 13.1083H106.693C107.231 13.1083 107.667 12.6723 107.667 12.1345C107.667 11.2668 108.716 10.8323 109.329 11.4458L110.613 12.7296C111.287 13.4036 111.287 14.4964 110.613 15.1704L109.333 16.45Z" fill="white" fill-opacity="0.48"/>
|
||||
<path d="M124 8C124 3.58172 127.582 0 132 0H204C208.418 0 212 3.58172 212 8V20C212 24.4183 208.418 28 204 28H132C127.582 28 124 24.4183 124 20V8Z" fill="#1C1C1C"/>
|
||||
<path d="M132 0.5H204C208.142 0.5 211.5 3.85786 211.5 8V20C211.5 24.1421 208.142 27.5 204 27.5H132C127.858 27.5 124.5 24.1421 124.5 20V8C124.5 3.85786 127.858 0.5 132 0.5Z" stroke="white" stroke-opacity="0.24"/>
|
||||
<rect x="132" y="8" width="12" height="12" rx="3" fill="white" fill-opacity="0.24"/>
|
||||
<rect x="152" y="8" width="12" height="12" rx="3" fill="#009AC7"/>
|
||||
<rect x="172" y="8" width="12" height="12" rx="3" fill="white" fill-opacity="0.24"/>
|
||||
<rect x="192" y="8" width="12" height="12" rx="3" fill="#009AC7"/>
|
||||
<path d="M237.5 12.3333C237.5 12.7936 237.127 13.1667 236.667 13.1667H223.333C222.873 13.1667 222.5 12.7936 222.5 12.3333C222.5 11.8731 222.873 11.5 223.333 11.5H236.667C237.127 11.5 237.5 11.8731 237.5 12.3333ZM237.5 15.6667C237.5 15.2064 237.127 14.8333 236.667 14.8333H223.333C222.873 14.8333 222.5 15.2064 222.5 15.6667C222.5 16.1269 222.873 16.5 223.333 16.5H236.667C237.127 16.5 237.5 16.1269 237.5 15.6667Z" fill="white" fill-opacity="0.48"/>
|
||||
<path d="M257.167 16.5H253L258.833 4.83337V11.5H263L257.167 23.1667V16.5Z" fill="white" fill-opacity="0.24"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 2.4 KiB |
@@ -1,17 +0,0 @@
|
||||
<svg width="268" height="28" viewBox="0 0 268 28" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="88" height="28" rx="8" fill="white"/>
|
||||
<rect x="0.5" y="0.5" width="87" height="27" rx="7.5" stroke="black" stroke-opacity="0.12"/>
|
||||
<rect x="8" y="8" width="12" height="12" rx="3" fill="black" fill-opacity="0.12"/>
|
||||
<rect x="28" y="8" width="12" height="12" rx="3" fill="black" fill-opacity="0.12"/>
|
||||
<rect x="48" y="8" width="12" height="12" rx="3" fill="black" fill-opacity="0.12"/>
|
||||
<rect x="68" y="8" width="12" height="12" rx="3" fill="black" fill-opacity="0.12"/>
|
||||
<path d="M107.667 18.1167V14.7833H100.233L100.208 13.1083H107.667V9.78333L111.833 13.95L107.667 18.1167Z" fill="#B1B1B1"/>
|
||||
<rect x="124" width="88" height="28" rx="8" fill="white"/>
|
||||
<rect x="124.5" y="0.5" width="87" height="27" rx="7.5" stroke="black" stroke-opacity="0.12"/>
|
||||
<rect x="132" y="8" width="12" height="12" rx="3" fill="black" fill-opacity="0.12"/>
|
||||
<rect x="152" y="8" width="12" height="12" rx="3" fill="black" fill-opacity="0.12"/>
|
||||
<rect x="172" y="8" width="12" height="12" rx="3" fill="#009AC7"/>
|
||||
<rect x="192" y="8" width="12" height="12" rx="3" fill="black" fill-opacity="0.12"/>
|
||||
<path d="M237.5 13.1667H222.5V11.5H237.5V13.1667ZM237.5 14.8333H222.5V16.5H237.5V14.8333Z" fill="#B1B1B1"/>
|
||||
<path d="M257.167 16.5H253L258.833 4.83337V11.5H263L257.167 23.1667V16.5Z" fill="#5E5E5E"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.4 KiB |
@@ -1,17 +0,0 @@
|
||||
<svg width="268" height="28" viewBox="0 0 268 28" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M0 8C0 3.58172 3.58172 0 8 0H80C84.4183 0 88 3.58172 88 8V20C88 24.4183 84.4183 28 80 28H8C3.58172 28 0 24.4183 0 20V8Z" fill="#1C1C1C"/>
|
||||
<path d="M8 0.5H80C84.1421 0.5 87.5 3.85786 87.5 8V20C87.5 24.1421 84.1421 27.5 80 27.5H8C3.85786 27.5 0.5 24.1421 0.5 20V8C0.5 3.85786 3.85786 0.5 8 0.5Z" stroke="white" stroke-opacity="0.24"/>
|
||||
<rect x="8" y="8" width="12" height="12" rx="3" fill="white" fill-opacity="0.24"/>
|
||||
<rect x="28" y="8" width="12" height="12" rx="3" fill="white" fill-opacity="0.24"/>
|
||||
<rect x="48" y="8" width="12" height="12" rx="3" fill="white" fill-opacity="0.24"/>
|
||||
<rect x="68" y="8" width="12" height="12" rx="3" fill="white" fill-opacity="0.24"/>
|
||||
<path d="M109.333 16.45C108.718 17.065 107.667 16.6294 107.667 15.7596C107.667 15.2204 107.23 14.7833 106.69 14.7833H101.058C100.601 14.7833 100.228 14.4159 100.221 13.9583C100.214 13.4909 100.591 13.1083 101.058 13.1083H106.693C107.231 13.1083 107.667 12.6723 107.667 12.1345C107.667 11.2668 108.716 10.8323 109.329 11.4458L110.613 12.7296C111.287 13.4036 111.287 14.4964 110.613 15.1704L109.333 16.45Z" fill="white" fill-opacity="0.48"/>
|
||||
<path d="M124 8C124 3.58172 127.582 0 132 0H204C208.418 0 212 3.58172 212 8V20C212 24.4183 208.418 28 204 28H132C127.582 28 124 24.4183 124 20V8Z" fill="#1C1C1C"/>
|
||||
<path d="M132 0.5H204C208.142 0.5 211.5 3.85786 211.5 8V20C211.5 24.1421 208.142 27.5 204 27.5H132C127.858 27.5 124.5 24.1421 124.5 20V8C124.5 3.85786 127.858 0.5 132 0.5Z" stroke="white" stroke-opacity="0.24"/>
|
||||
<rect x="132" y="8" width="12" height="12" rx="3" fill="white" fill-opacity="0.24"/>
|
||||
<rect x="152" y="8" width="12" height="12" rx="3" fill="white" fill-opacity="0.24"/>
|
||||
<rect x="172" y="8" width="12" height="12" rx="3" fill="#009AC7"/>
|
||||
<rect x="192" y="8" width="12" height="12" rx="3" fill="white" fill-opacity="0.24"/>
|
||||
<path d="M237.5 12.3333C237.5 12.7936 237.127 13.1667 236.667 13.1667H223.333C222.873 13.1667 222.5 12.7936 222.5 12.3333C222.5 11.8731 222.873 11.5 223.333 11.5H236.667C237.127 11.5 237.5 11.8731 237.5 12.3333ZM237.5 15.6667C237.5 15.2064 237.127 14.8333 236.667 14.8333H223.333C222.873 14.8333 222.5 15.2064 222.5 15.6667C222.5 16.1269 222.873 16.5 223.333 16.5H236.667C237.127 16.5 237.5 16.1269 237.5 15.6667Z" fill="white" fill-opacity="0.48"/>
|
||||
<path d="M257.167 16.5H253L258.833 4.83337V11.5H263L257.167 23.1667V16.5Z" fill="white" fill-opacity="0.24"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 2.4 KiB |
@@ -1,17 +0,0 @@
|
||||
<svg width="268" height="28" viewBox="0 0 268 28" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="88" height="28" rx="8" fill="white"/>
|
||||
<rect x="0.5" y="0.5" width="87" height="27" rx="7.5" stroke="black" stroke-opacity="0.12"/>
|
||||
<rect x="8" y="8" width="12" height="12" rx="3" fill="#009AC7"/>
|
||||
<rect x="28" y="8" width="12" height="12" rx="3" fill="#009AC7"/>
|
||||
<rect x="48" y="8" width="12" height="12" rx="3" fill="black" fill-opacity="0.12"/>
|
||||
<rect x="68" y="8" width="12" height="12" rx="3" fill="#009AC7"/>
|
||||
<path d="M107.667 18.1167V14.7833H100.233L100.208 13.1083H107.667V9.78333L111.833 13.95L107.667 18.1167Z" fill="#B1B1B1"/>
|
||||
<rect x="124" width="88" height="28" rx="8" fill="white"/>
|
||||
<rect x="124.5" y="0.5" width="87" height="27" rx="7.5" stroke="black" stroke-opacity="0.12"/>
|
||||
<rect x="132" y="8" width="12" height="12" rx="3" fill="#009AC7"/>
|
||||
<rect x="152" y="8" width="12" height="12" rx="3" fill="#009AC7"/>
|
||||
<rect x="172" y="8" width="12" height="12" rx="3" fill="#009AC7"/>
|
||||
<rect x="192" y="8" width="12" height="12" rx="3" fill="#009AC7"/>
|
||||
<path d="M237.5 13.1667H222.5V11.5H237.5V13.1667ZM237.5 14.8333H222.5V16.5H237.5V14.8333Z" fill="#B1B1B1"/>
|
||||
<path d="M257.167 16.5H253L258.833 4.83337V11.5H263L257.167 23.1667V16.5Z" fill="#5E5E5E"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.2 KiB |
@@ -1,17 +0,0 @@
|
||||
<svg width="268" height="28" viewBox="0 0 268 28" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M0 8C0 3.58172 3.58172 0 8 0H80C84.4183 0 88 3.58172 88 8V20C88 24.4183 84.4183 28 80 28H8C3.58172 28 0 24.4183 0 20V8Z" fill="#1C1C1C"/>
|
||||
<path d="M8 0.5H80C84.1421 0.5 87.5 3.85786 87.5 8V20C87.5 24.1421 84.1421 27.5 80 27.5H8C3.85786 27.5 0.5 24.1421 0.5 20V8C0.5 3.85786 3.85786 0.5 8 0.5Z" stroke="white" stroke-opacity="0.24"/>
|
||||
<rect x="8" y="8" width="12" height="12" rx="3" fill="#009AC7"/>
|
||||
<rect x="28" y="8" width="12" height="12" rx="3" fill="#009AC7"/>
|
||||
<rect x="48" y="8" width="12" height="12" rx="3" fill="white" fill-opacity="0.24"/>
|
||||
<rect x="68" y="8" width="12" height="12" rx="3" fill="#009AC7"/>
|
||||
<path d="M109.333 16.45C108.718 17.065 107.667 16.6294 107.667 15.7596C107.667 15.2204 107.23 14.7833 106.69 14.7833H101.058C100.601 14.7833 100.228 14.4159 100.221 13.9583C100.214 13.4909 100.591 13.1083 101.058 13.1083H106.693C107.231 13.1083 107.667 12.6723 107.667 12.1345C107.667 11.2668 108.716 10.8323 109.329 11.4458L110.613 12.7296C111.287 13.4036 111.287 14.4964 110.613 15.1704L109.333 16.45Z" fill="white" fill-opacity="0.48"/>
|
||||
<path d="M124 8C124 3.58172 127.582 0 132 0H204C208.418 0 212 3.58172 212 8V20C212 24.4183 208.418 28 204 28H132C127.582 28 124 24.4183 124 20V8Z" fill="#1C1C1C"/>
|
||||
<path d="M132 0.5H204C208.142 0.5 211.5 3.85786 211.5 8V20C211.5 24.1421 208.142 27.5 204 27.5H132C127.858 27.5 124.5 24.1421 124.5 20V8C124.5 3.85786 127.858 0.5 132 0.5Z" stroke="white" stroke-opacity="0.24"/>
|
||||
<rect x="132" y="8" width="12" height="12" rx="3" fill="#009AC7"/>
|
||||
<rect x="152" y="8" width="12" height="12" rx="3" fill="#009AC7"/>
|
||||
<rect x="172" y="8" width="12" height="12" rx="3" fill="#009AC7"/>
|
||||
<rect x="192" y="8" width="12" height="12" rx="3" fill="#009AC7"/>
|
||||
<path d="M237.5 12.3333C237.5 12.7936 237.127 13.1667 236.667 13.1667H223.333C222.873 13.1667 222.5 12.7936 222.5 12.3333C222.5 11.8731 222.873 11.5 223.333 11.5H236.667C237.127 11.5 237.5 11.8731 237.5 12.3333ZM237.5 15.6667C237.5 15.2064 237.127 14.8333 236.667 14.8333H223.333C222.873 14.8333 222.5 15.2064 222.5 15.6667C222.5 16.1269 222.873 16.5 223.333 16.5H236.667C237.127 16.5 237.5 16.1269 237.5 15.6667Z" fill="white" fill-opacity="0.48"/>
|
||||
<path d="M257.167 16.5H253L258.833 4.83337V11.5H263L257.167 23.1667V16.5Z" fill="white" fill-opacity="0.24"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 2.3 KiB |
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "home-assistant-frontend"
|
||||
version = "20260429.0"
|
||||
version = "20260325.0"
|
||||
license = "Apache-2.0"
|
||||
license-files = ["LICENSE*"]
|
||||
description = "The Home Assistant frontend"
|
||||
|
||||
@@ -36,9 +36,6 @@ export class HaSelectBox extends LitElement {
|
||||
@property({ type: Number, attribute: "max_columns" })
|
||||
public maxColumns?: number;
|
||||
|
||||
@property({ type: Boolean, attribute: "stacked_image" })
|
||||
public stackedImage = false;
|
||||
|
||||
render() {
|
||||
const maxColumns = this.maxColumns ?? 3;
|
||||
const columns = Math.min(maxColumns, this.options.length);
|
||||
@@ -51,8 +48,7 @@ export class HaSelectBox extends LitElement {
|
||||
}
|
||||
|
||||
private _renderOption(option: SelectBoxOption) {
|
||||
const horizontal = this.maxColumns === 1 && !this.stackedImage;
|
||||
const stacked = this.maxColumns === 1 && this.stackedImage;
|
||||
const horizontal = this.maxColumns === 1;
|
||||
const disabled = option.disabled || this.disabled || false;
|
||||
const selected = option.value === this.value;
|
||||
|
||||
@@ -70,7 +66,6 @@ export class HaSelectBox extends LitElement {
|
||||
<label
|
||||
class="option ${classMap({
|
||||
horizontal: horizontal,
|
||||
stacked: stacked,
|
||||
selected: selected,
|
||||
})}"
|
||||
?disabled=${disabled}
|
||||
@@ -192,16 +187,6 @@ export class HaSelectBox extends LitElement {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.option.stacked {
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.option.stacked img {
|
||||
max-width: 100%;
|
||||
max-height: var(--ha-select-box-image-size, 96px);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.option:before {
|
||||
content: "";
|
||||
display: block;
|
||||
|
||||
@@ -1,123 +0,0 @@
|
||||
import { LitElement, css, html, nothing } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
import type { LocalizeKeys } from "../../common/translations/localize";
|
||||
import type {
|
||||
AutomationBehavior,
|
||||
AutomationBehaviorConditionMode,
|
||||
AutomationBehaviorSelector,
|
||||
AutomationBehaviorTriggerMode,
|
||||
} from "../../data/selector";
|
||||
import type { HomeAssistant } from "../../types";
|
||||
import "../ha-input-helper-text";
|
||||
import type { SelectBoxOption } from "../ha-select-box";
|
||||
import "../ha-select-box";
|
||||
|
||||
const TRIGGER_BEHAVIORS: AutomationBehaviorTriggerMode[] = [
|
||||
"any",
|
||||
"first",
|
||||
"last",
|
||||
];
|
||||
|
||||
const CONDITION_BEHAVIORS: AutomationBehaviorConditionMode[] = ["any", "all"];
|
||||
|
||||
@customElement("ha-selector-automation_behavior")
|
||||
export class HaSelectorAutomationBehavior extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ attribute: false })
|
||||
public selector!: AutomationBehaviorSelector;
|
||||
|
||||
@property() public value?: AutomationBehavior;
|
||||
|
||||
@property() public helper?: string;
|
||||
|
||||
@property({ attribute: false })
|
||||
public localizeValue?: (key: string) => string;
|
||||
|
||||
@property({ type: Boolean }) public disabled = false;
|
||||
|
||||
@property({ type: Boolean }) public required = true;
|
||||
|
||||
protected render() {
|
||||
const { mode } = this.selector.automation_behavior ?? {};
|
||||
const modeKey = mode ?? "trigger";
|
||||
|
||||
const isTrigger = modeKey === "trigger";
|
||||
|
||||
const options = this._behaviors().map<SelectBoxOption>((behavior) => ({
|
||||
value: behavior,
|
||||
label: this._localizeOption(behavior, "label"),
|
||||
description: this._localizeOption(behavior, "description"),
|
||||
disabled: this.disabled,
|
||||
...(isTrigger && {
|
||||
image: {
|
||||
src: `/static/images/form/automation_behavior_trigger_${behavior}.svg`,
|
||||
src_dark: `/static/images/form/automation_behavior_trigger_${behavior}_dark.svg`,
|
||||
},
|
||||
}),
|
||||
}));
|
||||
|
||||
return html`
|
||||
<ha-select-box
|
||||
.hass=${this.hass}
|
||||
.options=${options}
|
||||
.value=${this.value ?? ""}
|
||||
max_columns="1"
|
||||
?stacked_image=${isTrigger}
|
||||
@value-changed=${this._valueChanged}
|
||||
></ha-select-box>
|
||||
${this.helper
|
||||
? html`<ha-input-helper-text .disabled=${this.disabled}
|
||||
>${this.helper}</ha-input-helper-text
|
||||
>`
|
||||
: nothing}
|
||||
`;
|
||||
}
|
||||
|
||||
private _behaviors(): AutomationBehavior[] {
|
||||
const mode = this.selector.automation_behavior?.mode;
|
||||
return mode === "condition" ? CONDITION_BEHAVIORS : TRIGGER_BEHAVIORS;
|
||||
}
|
||||
|
||||
private _localizeOption(
|
||||
behavior: AutomationBehavior,
|
||||
field: "label" | "description"
|
||||
): string {
|
||||
const { translation_key: translationKey, mode } =
|
||||
this.selector.automation_behavior ?? {};
|
||||
|
||||
if (this.localizeValue && translationKey) {
|
||||
const translated = this.localizeValue(
|
||||
`${translationKey}.options.${behavior}.${field}`
|
||||
);
|
||||
if (translated) {
|
||||
return translated;
|
||||
}
|
||||
}
|
||||
return this.hass.localize(
|
||||
`ui.components.selectors.automation_behavior.${mode ?? "trigger"}.options.${behavior}.${field}` as LocalizeKeys
|
||||
);
|
||||
}
|
||||
|
||||
private _valueChanged(ev: CustomEvent) {
|
||||
ev.stopPropagation();
|
||||
const value = ev.detail.value as AutomationBehavior;
|
||||
if (this.disabled || value === this.value) {
|
||||
return;
|
||||
}
|
||||
fireEvent(this, "value-changed", { value });
|
||||
}
|
||||
|
||||
static styles = css`
|
||||
ha-select-box {
|
||||
--ha-select-box-image-size: 28px;
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ha-selector-automation_behavior": HaSelectorAutomationBehavior;
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,6 @@ import type { HomeAssistant } from "../../types";
|
||||
const LOAD_ELEMENTS = {
|
||||
action: () => import("./ha-selector-action"),
|
||||
addon: () => import("./ha-selector-addon"),
|
||||
automation_behavior: () => import("./ha-selector-automation-behavior"),
|
||||
app: () => import("./ha-selector-app"),
|
||||
area: () => import("./ha-selector-area"),
|
||||
areas_display: () => import("./ha-selector-areas-display"),
|
||||
|
||||
@@ -322,7 +322,6 @@ export interface ShorthandNotCondition extends ShorthandBaseCondition {
|
||||
|
||||
export interface AutomationElementGroupCollection {
|
||||
titleKey?: LocalizeKeys;
|
||||
generic?: boolean;
|
||||
groups: AutomationElementGroup;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,33 +8,30 @@ import type { Selector, TargetSelector } from "./selector";
|
||||
export const CONDITION_COLLECTIONS: AutomationElementGroupCollection[] = [
|
||||
{
|
||||
groups: {
|
||||
device: {},
|
||||
dynamicGroups: {},
|
||||
entity: { icon: mdiShape, members: { state: {}, numeric_state: {} } },
|
||||
time_location: {
|
||||
icon: mdiMapClock,
|
||||
members: { sun: {}, time: {}, zone: {} },
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
titleKey:
|
||||
"ui.panel.config.automation.editor.conditions.groups.helpers.label",
|
||||
groups: {
|
||||
helpers: {},
|
||||
},
|
||||
},
|
||||
{
|
||||
titleKey: "ui.panel.config.automation.editor.conditions.groups.other.label",
|
||||
groups: {
|
||||
template: {},
|
||||
trigger: {},
|
||||
other: {},
|
||||
},
|
||||
},
|
||||
{
|
||||
titleKey:
|
||||
"ui.panel.config.automation.editor.conditions.groups.generic.label",
|
||||
generic: true,
|
||||
groups: {
|
||||
device: {},
|
||||
entity: { icon: mdiShape, members: { state: {}, numeric_state: {} } },
|
||||
},
|
||||
},
|
||||
{
|
||||
titleKey:
|
||||
"ui.panel.config.automation.editor.conditions.groups.custom_integrations.label",
|
||||
groups: {
|
||||
customDynamicGroups: {},
|
||||
},
|
||||
},
|
||||
] as const;
|
||||
|
||||
export const CONDITION_BUILDING_BLOCKS_GROUP = {
|
||||
|
||||
@@ -31,7 +31,6 @@ export type Selector =
|
||||
| AreaSelector
|
||||
| AreasDisplaySelector
|
||||
| AttributeSelector
|
||||
| AutomationBehaviorSelector
|
||||
| BooleanSelector
|
||||
| ButtonToggleSelector
|
||||
| ChooseSelector
|
||||
@@ -125,21 +124,6 @@ export interface BooleanSelector {
|
||||
boolean: {} | null;
|
||||
}
|
||||
|
||||
export type AutomationBehaviorTriggerMode = "first" | "last" | "any";
|
||||
|
||||
export type AutomationBehaviorConditionMode = "all" | "any";
|
||||
|
||||
export type AutomationBehavior =
|
||||
| AutomationBehaviorTriggerMode
|
||||
| AutomationBehaviorConditionMode;
|
||||
|
||||
export interface AutomationBehaviorSelector {
|
||||
automation_behavior: {
|
||||
mode: "trigger" | "condition";
|
||||
translation_key?: string;
|
||||
} | null;
|
||||
}
|
||||
|
||||
export interface ButtonToggleSelector {
|
||||
button_toggle: {
|
||||
options: readonly string[] | readonly SelectOption[];
|
||||
|
||||
@@ -13,7 +13,9 @@ import type { Selector, TargetSelector } from "./selector";
|
||||
export const TRIGGER_COLLECTIONS: AutomationElementGroupCollection[] = [
|
||||
{
|
||||
groups: {
|
||||
device: {},
|
||||
dynamicGroups: {},
|
||||
entity: { icon: mdiShape, members: { state: {}, numeric_state: {} } },
|
||||
time_location: {
|
||||
icon: mdiMapClock,
|
||||
members: {
|
||||
@@ -24,6 +26,17 @@ export const TRIGGER_COLLECTIONS: AutomationElementGroupCollection[] = [
|
||||
zone: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
titleKey: "ui.panel.config.automation.editor.triggers.groups.helpers.label",
|
||||
groups: {
|
||||
helpers: {},
|
||||
},
|
||||
},
|
||||
{
|
||||
titleKey: "ui.panel.config.automation.editor.triggers.groups.other.label",
|
||||
groups: {
|
||||
event: {},
|
||||
geo_location: {},
|
||||
homeassistant: {},
|
||||
@@ -32,25 +45,9 @@ export const TRIGGER_COLLECTIONS: AutomationElementGroupCollection[] = [
|
||||
template: {},
|
||||
webhook: {},
|
||||
persistent_notification: {},
|
||||
helpers: {},
|
||||
other: {},
|
||||
},
|
||||
},
|
||||
{
|
||||
titleKey: "ui.panel.config.automation.editor.triggers.groups.generic.label",
|
||||
generic: true,
|
||||
groups: {
|
||||
device: {},
|
||||
entity: { icon: mdiShape, members: { state: {}, numeric_state: {} } },
|
||||
},
|
||||
},
|
||||
{
|
||||
titleKey:
|
||||
"ui.panel.config.automation.editor.triggers.groups.custom_integrations.label",
|
||||
groups: {
|
||||
customDynamicGroups: {},
|
||||
},
|
||||
},
|
||||
] as const;
|
||||
|
||||
export const isTriggerList = (trigger: Trigger): trigger is TriggerList =>
|
||||
|
||||
@@ -146,7 +146,6 @@ const TYPES = {
|
||||
export interface CollectionGroup {
|
||||
collectionIndex: number;
|
||||
titleKey?: LocalizeKeys;
|
||||
generic?: boolean;
|
||||
groups: AddAutomationElementListItem[];
|
||||
}
|
||||
|
||||
@@ -177,16 +176,9 @@ const ENTITY_DOMAINS_OTHER = new Set([
|
||||
|
||||
const ENTITY_DOMAINS_MAIN = new Set(["notify"]);
|
||||
|
||||
const DYNAMIC_KEYWORDS = [
|
||||
"dynamicGroups",
|
||||
"helpers",
|
||||
"other",
|
||||
"customDynamicGroups",
|
||||
];
|
||||
const DYNAMIC_KEYWORDS = ["dynamicGroups", "helpers", "other"];
|
||||
|
||||
const DYNAMIC_TO_GENERIC = new Set([`${DYNAMIC_PREFIX}event`]);
|
||||
|
||||
type CollectionGroupType = "helper" | "other" | "dynamic" | "customDynamic";
|
||||
const GENERIC_GROUPS = new Set(["device", "entity", `${DYNAMIC_PREFIX}event`]);
|
||||
|
||||
@customElement("add-automation-element-dialog")
|
||||
class DialogAddAutomationElement
|
||||
@@ -1094,27 +1086,10 @@ class DialogAddAutomationElement
|
||||
): CollectionGroup[] => {
|
||||
const generatedCollections: CollectionGroup[] = [];
|
||||
|
||||
let genericCollectionIndex = -1;
|
||||
let dynamicCollectionIndex = -1;
|
||||
|
||||
collections.forEach((collection, index) => {
|
||||
let collectionGroups = Object.entries(collection.groups);
|
||||
const groups: AddAutomationElementListItem[] = [];
|
||||
|
||||
const types: CollectionGroupType[] = [];
|
||||
if (collection.groups.dynamicGroups) {
|
||||
types.push("dynamic");
|
||||
}
|
||||
if (collection.groups.helpers) {
|
||||
types.push("helper");
|
||||
}
|
||||
if (collection.groups.other) {
|
||||
types.push("other");
|
||||
}
|
||||
if (collection.groups.customDynamicGroups) {
|
||||
types.push("customDynamic");
|
||||
}
|
||||
|
||||
if (
|
||||
type === "trigger" &&
|
||||
Object.keys(collection.groups).some((item) =>
|
||||
@@ -1127,7 +1102,11 @@ class DialogAddAutomationElement
|
||||
triggerDescriptions,
|
||||
manifests,
|
||||
domains,
|
||||
types
|
||||
collection.groups.dynamicGroups
|
||||
? undefined
|
||||
: collection.groups.helpers
|
||||
? "helper"
|
||||
: "other"
|
||||
)
|
||||
);
|
||||
|
||||
@@ -1146,7 +1125,11 @@ class DialogAddAutomationElement
|
||||
conditionDescriptions,
|
||||
manifests,
|
||||
domains,
|
||||
types
|
||||
collection.groups.dynamicGroups
|
||||
? undefined
|
||||
: collection.groups.helpers
|
||||
? "helper"
|
||||
: "other"
|
||||
)
|
||||
);
|
||||
|
||||
@@ -1184,45 +1167,55 @@ class DialogAddAutomationElement
|
||||
)
|
||||
);
|
||||
|
||||
if (groups.length) {
|
||||
if (collection.generic) {
|
||||
genericCollectionIndex = index;
|
||||
}
|
||||
if (collection.groups.dynamicGroups) {
|
||||
dynamicCollectionIndex = index;
|
||||
}
|
||||
|
||||
generatedCollections.push({
|
||||
collectionIndex: index,
|
||||
titleKey: collection.titleKey,
|
||||
generic: collection.generic,
|
||||
groups: groups.sort((a, b) => {
|
||||
return stringCompare(a.name, b.name, this.hass.locale.language);
|
||||
}),
|
||||
});
|
||||
}
|
||||
generatedCollections.push({
|
||||
collectionIndex: index,
|
||||
titleKey: collection.titleKey,
|
||||
groups: groups.sort((a, b) => {
|
||||
// make sure device is always on top
|
||||
if (a.key === "device" || a.key === "device_id") {
|
||||
return -1;
|
||||
}
|
||||
if (b.key === "device" || b.key === "device_id") {
|
||||
return 1;
|
||||
}
|
||||
return stringCompare(a.name, b.name, this.hass.locale.language);
|
||||
}),
|
||||
});
|
||||
});
|
||||
|
||||
// move groups from dynamic to generic
|
||||
if (genericCollectionIndex !== -1 && dynamicCollectionIndex !== -1) {
|
||||
const groupsToMove =
|
||||
generatedCollections[dynamicCollectionIndex].groups.filter((group) =>
|
||||
DYNAMIC_TO_GENERIC.has(group.key)
|
||||
) || [];
|
||||
generatedCollections[dynamicCollectionIndex].groups =
|
||||
generatedCollections[dynamicCollectionIndex].groups.filter(
|
||||
(group) => !DYNAMIC_TO_GENERIC.has(group.key)
|
||||
) || [];
|
||||
return !["trigger", "condition"].includes(type)
|
||||
? generatedCollections
|
||||
: generatedCollections.flatMap(
|
||||
(collection: CollectionGroup): CollectionGroup[] => {
|
||||
const genericGroups = collection.groups.filter((group) =>
|
||||
GENERIC_GROUPS.has(group.key)
|
||||
);
|
||||
|
||||
generatedCollections[genericCollectionIndex].groups = [
|
||||
...(generatedCollections[genericCollectionIndex].groups || []),
|
||||
...groupsToMove,
|
||||
].sort((a, b) =>
|
||||
stringCompare(a.name, b.name, this.hass.locale.language)
|
||||
);
|
||||
}
|
||||
const mainGroups = collection.groups.filter(
|
||||
(group) => !GENERIC_GROUPS.has(group.key)
|
||||
);
|
||||
|
||||
return generatedCollections;
|
||||
return [
|
||||
...(mainGroups.length
|
||||
? [
|
||||
{
|
||||
...collection,
|
||||
groups: mainGroups,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
...(genericGroups.length
|
||||
? [
|
||||
{
|
||||
collectionIndex: collection.collectionIndex,
|
||||
titleKey: "ui.panel.config.automation.editor.generic",
|
||||
groups: genericGroups,
|
||||
} satisfies CollectionGroup,
|
||||
]
|
||||
: []),
|
||||
];
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
@@ -1370,31 +1363,34 @@ class DialogAddAutomationElement
|
||||
domain: string,
|
||||
manifest: DomainManifestLookup[string] | undefined,
|
||||
domainUsed: boolean,
|
||||
types: CollectionGroupType[]
|
||||
type: "helper" | "other" | undefined
|
||||
): boolean {
|
||||
const matchDynamic =
|
||||
((types.includes("dynamic") && (!manifest || manifest.is_built_in)) ||
|
||||
(types.includes("customDynamic") &&
|
||||
!(manifest?.is_built_in ?? true))) &&
|
||||
(ENTITY_DOMAINS_MAIN.has(domain) ||
|
||||
if (type === undefined) {
|
||||
return (
|
||||
ENTITY_DOMAINS_MAIN.has(domain) ||
|
||||
(manifest?.integration_type === "entity" &&
|
||||
!ENTITY_DOMAINS_OTHER.has(domain) &&
|
||||
(domainUsed || (this._systemDomains?.active.has(domain) ?? false))) ||
|
||||
(manifest?.integration_type === "system" &&
|
||||
(this._systemDomains?.active.has(domain) ?? false)));
|
||||
|
||||
const matchHelper =
|
||||
types.includes("helper") && manifest?.integration_type === "helper";
|
||||
|
||||
const matchOther =
|
||||
types.includes("other") &&
|
||||
(this._systemDomains?.active.has(domain) ?? false))
|
||||
);
|
||||
}
|
||||
if (type === "helper") {
|
||||
return manifest?.integration_type === "helper";
|
||||
}
|
||||
// type === "other"
|
||||
return (
|
||||
!ENTITY_DOMAINS_MAIN.has(domain) &&
|
||||
(ENTITY_DOMAINS_OTHER.has(domain) ||
|
||||
(!domainUsed &&
|
||||
manifest?.integration_type === "entity" &&
|
||||
!(this._systemDomains?.active.has(domain) ?? false)) ||
|
||||
(manifest?.integration_type === "system" &&
|
||||
!(this._systemDomains?.active.has(domain) ?? false)) ||
|
||||
!["helper", "entity", "system"].includes(
|
||||
manifest?.integration_type || ""
|
||||
));
|
||||
|
||||
return matchDynamic || matchHelper || matchOther;
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
private _triggerGroups = (
|
||||
@@ -1402,7 +1398,7 @@ class DialogAddAutomationElement
|
||||
triggers: TriggerDescriptions,
|
||||
manifests: DomainManifestLookup | undefined,
|
||||
domains: Set<string> | undefined,
|
||||
types: CollectionGroupType[]
|
||||
type: "helper" | "other" | undefined
|
||||
): AddAutomationElementListItem[] => {
|
||||
if (!triggers || !manifests) {
|
||||
return [];
|
||||
@@ -1420,7 +1416,7 @@ class DialogAddAutomationElement
|
||||
const manifest = manifests[domain];
|
||||
const domainUsed = !domains ? true : domains.has(domain);
|
||||
|
||||
if (this._domainMatchesGroupType(domain, manifest, domainUsed, types)) {
|
||||
if (this._domainMatchesGroupType(domain, manifest, domainUsed, type)) {
|
||||
result.push({
|
||||
icon: html`
|
||||
<ha-domain-icon .domain=${domain} brand-fallback></ha-domain-icon>
|
||||
@@ -1474,7 +1470,7 @@ class DialogAddAutomationElement
|
||||
conditions: ConditionDescriptions,
|
||||
manifests: DomainManifestLookup | undefined,
|
||||
domains: Set<string> | undefined,
|
||||
types: CollectionGroupType[]
|
||||
type: "helper" | "other" | undefined
|
||||
): AddAutomationElementListItem[] => {
|
||||
if (!conditions || !manifests) {
|
||||
return [];
|
||||
@@ -1492,7 +1488,7 @@ class DialogAddAutomationElement
|
||||
const manifest = manifests[domain];
|
||||
const domainUsed = !domains ? true : domains.has(domain);
|
||||
|
||||
if (this._domainMatchesGroupType(domain, manifest, domainUsed, types)) {
|
||||
if (this._domainMatchesGroupType(domain, manifest, domainUsed, type)) {
|
||||
result.push({
|
||||
icon: html`
|
||||
<ha-domain-icon .domain=${domain} brand-fallback></ha-domain-icon>
|
||||
|
||||
@@ -100,8 +100,7 @@ export class HaPlatformCondition extends LitElement {
|
||||
field.default !== undefined &&
|
||||
updatedOptions[key] === undefined &&
|
||||
!(
|
||||
field.selector &&
|
||||
"automation_behavior" in field.selector &&
|
||||
key === "behavior" &&
|
||||
this.description?.target &&
|
||||
!this.condition?.target
|
||||
)
|
||||
@@ -228,7 +227,7 @@ export class HaPlatformCondition extends LitElement {
|
||||
}
|
||||
|
||||
if (
|
||||
"automation_behavior" in selector &&
|
||||
fieldName === "behavior" &&
|
||||
this.description?.target &&
|
||||
(!this.condition?.target ||
|
||||
(this._resolvedTargetEntityCount !== undefined &&
|
||||
@@ -438,32 +437,17 @@ export class HaPlatformCondition extends LitElement {
|
||||
) {
|
||||
this._resolvedTargetEntityCount = getTargetEntityCount(target);
|
||||
|
||||
const behaviorFieldEntry = Object.entries(
|
||||
this.description?.fields ?? {}
|
||||
).find(
|
||||
([, field]) => field.selector && "automation_behavior" in field.selector
|
||||
);
|
||||
|
||||
if (!behaviorFieldEntry) {
|
||||
return;
|
||||
}
|
||||
|
||||
const [behaviorFieldName, behaviorField] = behaviorFieldEntry;
|
||||
|
||||
if (
|
||||
target &&
|
||||
this._resolvedTargetEntityCount > 1 &&
|
||||
this.condition.options?.[behaviorFieldName] === undefined
|
||||
this.condition.options?.behavior === undefined
|
||||
) {
|
||||
const behaviorDefault = behaviorField.default;
|
||||
const behaviorDefault = this.description?.fields?.behavior?.default;
|
||||
if (behaviorDefault !== undefined) {
|
||||
fireEvent(this, "value-changed", {
|
||||
value: {
|
||||
...this.condition,
|
||||
options: {
|
||||
...this.condition.options,
|
||||
[behaviorFieldName]: behaviorDefault,
|
||||
},
|
||||
options: { ...this.condition.options, behavior: behaviorDefault },
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -135,8 +135,7 @@ export class HaPlatformTrigger extends LitElement {
|
||||
field.default !== undefined &&
|
||||
updatedOptions[key] === undefined &&
|
||||
!(
|
||||
field.selector &&
|
||||
"automation_behavior" in field.selector &&
|
||||
key === "behavior" &&
|
||||
this.description?.target &&
|
||||
!this.trigger?.target
|
||||
)
|
||||
@@ -263,7 +262,7 @@ export class HaPlatformTrigger extends LitElement {
|
||||
}
|
||||
|
||||
if (
|
||||
"automation_behavior" in selector &&
|
||||
fieldName === "behavior" &&
|
||||
this.description?.target &&
|
||||
(!this.trigger?.target ||
|
||||
(this._resolvedTargetEntityCount !== undefined &&
|
||||
@@ -471,32 +470,17 @@ export class HaPlatformTrigger extends LitElement {
|
||||
private _updateResolvedTargetEntityCount(target: PlatformTrigger["target"]) {
|
||||
this._resolvedTargetEntityCount = getTargetEntityCount(target);
|
||||
|
||||
const behaviorFieldEntry = Object.entries(
|
||||
this.description?.fields ?? {}
|
||||
).find(
|
||||
([, field]) => field.selector && "automation_behavior" in field.selector
|
||||
);
|
||||
|
||||
if (!behaviorFieldEntry) {
|
||||
return;
|
||||
}
|
||||
|
||||
const [behaviorFieldName, behaviorField] = behaviorFieldEntry;
|
||||
|
||||
if (
|
||||
target &&
|
||||
this._resolvedTargetEntityCount > 1 &&
|
||||
this.trigger.options?.[behaviorFieldName] === undefined
|
||||
this.trigger.options?.behavior === undefined
|
||||
) {
|
||||
const behaviorDefault = behaviorField.default;
|
||||
const behaviorDefault = this.description?.fields?.behavior?.default;
|
||||
if (behaviorDefault !== undefined) {
|
||||
fireEvent(this, "value-changed", {
|
||||
value: {
|
||||
...this.trigger,
|
||||
options: {
|
||||
...this.trigger.options,
|
||||
[behaviorFieldName]: behaviorDefault,
|
||||
},
|
||||
options: { ...this.trigger.options, behavior: behaviorDefault },
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -669,9 +669,6 @@ class HaPanelDevAction extends LitElement {
|
||||
}
|
||||
.button-row {
|
||||
padding: var(--ha-space-2) var(--ha-space-4);
|
||||
border-top: 1px solid var(--divider-color);
|
||||
border-bottom: 1px solid var(--divider-color);
|
||||
background: var(--card-background-color);
|
||||
position: sticky;
|
||||
bottom: 0;
|
||||
box-sizing: border-box;
|
||||
|
||||
@@ -18,7 +18,7 @@ import "./events-list";
|
||||
class HaPanelDevEvent extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ type: Boolean, reflect: true }) public narrow = false;
|
||||
@property({ type: Boolean }) public narrow = false;
|
||||
|
||||
@state() private _eventType = "";
|
||||
|
||||
@@ -94,7 +94,6 @@ class HaPanelDevEvent extends LitElement {
|
||||
|
||||
<event-subscribe-card
|
||||
.hass=${this.hass}
|
||||
.narrow=${this.narrow}
|
||||
.selectedEventType=${this._selectedEventType}
|
||||
></event-subscribe-card>
|
||||
</div>
|
||||
@@ -159,8 +158,6 @@ class HaPanelDevEvent extends LitElement {
|
||||
padding: var(--ha-space-4);
|
||||
max-width: 1200px;
|
||||
margin: auto;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
:host {
|
||||
@@ -168,26 +165,10 @@ class HaPanelDevEvent extends LitElement {
|
||||
-webkit-user-select: initial;
|
||||
-moz-user-select: initial;
|
||||
display: block;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
:host([narrow]) {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
:host([narrow]) .content {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.flex {
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
:host([narrow]) .flex {
|
||||
min-height: auto;
|
||||
}
|
||||
|
||||
.inputs {
|
||||
@@ -199,19 +180,11 @@ class HaPanelDevEvent extends LitElement {
|
||||
}
|
||||
|
||||
event-subscribe-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 0;
|
||||
flex: 1;
|
||||
display: block;
|
||||
margin-top: var(--ha-space-4);
|
||||
direction: var(--direction);
|
||||
}
|
||||
|
||||
:host([narrow]) event-subscribe-card {
|
||||
flex: none;
|
||||
min-height: auto;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
@@ -1,39 +1,21 @@
|
||||
import {
|
||||
mdiChevronDoubleLeft,
|
||||
mdiChevronDoubleRight,
|
||||
mdiChevronLeft,
|
||||
mdiChevronRight,
|
||||
mdiInformationOutline,
|
||||
} from "@mdi/js";
|
||||
import type { HassEvent } from "home-assistant-js-websocket";
|
||||
import type { TemplateResult, PropertyValues } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { css, html, LitElement } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { formatTimeWithSeconds } from "../../../../common/datetime/format_time";
|
||||
import { repeat } from "lit/directives/repeat";
|
||||
import { formatTime } from "../../../../common/datetime/format_time";
|
||||
import "../../../../components/ha-alert";
|
||||
import "../../../../components/ha-button";
|
||||
import "../../../../components/ha-card";
|
||||
import "../../../../components/ha-icon-button";
|
||||
import "../../../../components/ha-svg-icon";
|
||||
import "../../../../components/ha-tooltip";
|
||||
import "../../../../components/ha-yaml-editor";
|
||||
import "../../../../components/input/ha-input";
|
||||
import type { HaInput } from "../../../../components/input/ha-input";
|
||||
import type { HomeAssistant } from "../../../../types";
|
||||
|
||||
const MAX_BUFFERED_EVENTS = 100;
|
||||
|
||||
interface SubscribedEvent {
|
||||
id: number;
|
||||
event: HassEvent;
|
||||
}
|
||||
|
||||
@customElement("event-subscribe-card")
|
||||
class EventSubscribeCard extends LitElement {
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@property({ type: Boolean, reflect: true }) public narrow = false;
|
||||
|
||||
@property({ attribute: false }) public selectedEventType = "";
|
||||
|
||||
@state() private _eventType = "";
|
||||
@@ -42,12 +24,13 @@ class EventSubscribeCard extends LitElement {
|
||||
|
||||
@state() private _eventFilter = "";
|
||||
|
||||
@state() private _events: SubscribedEvent[] = [];
|
||||
@state() private _events: {
|
||||
id: number;
|
||||
event: HassEvent;
|
||||
}[] = [];
|
||||
|
||||
@state() private _error?: string;
|
||||
|
||||
@state() private _viewedEventId?: number;
|
||||
|
||||
private _eventCount = 0;
|
||||
|
||||
@state() _ignoredEventsCount = 0;
|
||||
@@ -130,161 +113,43 @@ class EventSubscribeCard extends LitElement {
|
||||
</ha-button>
|
||||
</div>
|
||||
</ha-card>
|
||||
${this._renderEventsCard()}
|
||||
`;
|
||||
}
|
||||
|
||||
private _renderEventsCard(): TemplateResult {
|
||||
if (!this._events.length) {
|
||||
const message = this._subscribed
|
||||
? this.hass!.localize(
|
||||
"ui.panel.config.developer-tools.tabs.events.waiting_for_events"
|
||||
)
|
||||
: this.hass!.localize(
|
||||
"ui.panel.config.developer-tools.tabs.events.subscribe_prompt"
|
||||
);
|
||||
return html`
|
||||
<ha-card class="events-card">
|
||||
<div class="empty-state">${message}</div>
|
||||
</ha-card>
|
||||
`;
|
||||
}
|
||||
const bufferTotal = this._events.length;
|
||||
const index = this._resolveViewedIndex();
|
||||
const event = this._events[index];
|
||||
const position = event.id + 1;
|
||||
|
||||
const bufferPosition = bufferTotal - index;
|
||||
const atNewest = index === 0;
|
||||
|
||||
const hasRolledOver = this._events[bufferTotal - 1].id > 0;
|
||||
|
||||
return html`
|
||||
<ha-card class="events-card">
|
||||
<div class="events-toolbar">
|
||||
<ha-icon-button
|
||||
.path=${mdiChevronDoubleLeft}
|
||||
.disabled=${index >= bufferTotal - 1}
|
||||
.label=${this.hass!.localize(
|
||||
"ui.panel.config.developer-tools.tabs.events.oldest_event"
|
||||
<ha-card>
|
||||
<div class="card-content">
|
||||
<div class="events">
|
||||
${repeat(
|
||||
this._events,
|
||||
(event) => event.id,
|
||||
(event) => html`
|
||||
<div class="event">
|
||||
${this.hass!.localize(
|
||||
"ui.panel.config.developer-tools.tabs.events.event_fired",
|
||||
{ name: event.id }
|
||||
)}
|
||||
${formatTime(
|
||||
new Date(event.event.time_fired),
|
||||
this.hass!.locale,
|
||||
this.hass!.config
|
||||
)}:
|
||||
<ha-yaml-editor
|
||||
.hass=${this.hass}
|
||||
.defaultValue=${event.event}
|
||||
read-only
|
||||
></ha-yaml-editor>
|
||||
</div>
|
||||
`
|
||||
)}
|
||||
@click=${this._showOldest}
|
||||
></ha-icon-button>
|
||||
<ha-icon-button
|
||||
.path=${mdiChevronLeft}
|
||||
.disabled=${index >= bufferTotal - 1}
|
||||
.label=${this.hass!.localize(
|
||||
"ui.panel.config.developer-tools.tabs.events.older_event"
|
||||
)}
|
||||
@click=${this._showOlder}
|
||||
></ha-icon-button>
|
||||
<div class="event-info">
|
||||
${this.hass!.localize(
|
||||
"ui.panel.config.developer-tools.tabs.events.event_fired",
|
||||
{
|
||||
name: position,
|
||||
time: formatTimeWithSeconds(
|
||||
new Date(event.event.time_fired),
|
||||
this.hass!.locale,
|
||||
this.hass!.config
|
||||
),
|
||||
}
|
||||
)}
|
||||
<span class="counter">(${bufferPosition} / ${bufferTotal})</span>
|
||||
${hasRolledOver
|
||||
? html`
|
||||
<ha-svg-icon
|
||||
id="buffer-info"
|
||||
class="buffer-info"
|
||||
.path=${mdiInformationOutline}
|
||||
></ha-svg-icon>
|
||||
<ha-tooltip for="buffer-info" placement="bottom">
|
||||
<span class="buffer-tooltip">
|
||||
${this.hass!.localize(
|
||||
"ui.panel.config.developer-tools.tabs.events.buffer_disclaimer",
|
||||
{ count: MAX_BUFFERED_EVENTS }
|
||||
)}
|
||||
</span>
|
||||
</ha-tooltip>
|
||||
`
|
||||
: nothing}
|
||||
</div>
|
||||
<ha-icon-button
|
||||
.path=${mdiChevronRight}
|
||||
.disabled=${atNewest}
|
||||
.label=${this.hass!.localize(
|
||||
"ui.panel.config.developer-tools.tabs.events.newer_event"
|
||||
)}
|
||||
@click=${this._showNewer}
|
||||
></ha-icon-button>
|
||||
<ha-icon-button
|
||||
.path=${mdiChevronDoubleRight}
|
||||
.disabled=${atNewest}
|
||||
.label=${this.hass!.localize(
|
||||
"ui.panel.config.developer-tools.tabs.events.newest_event"
|
||||
)}
|
||||
@click=${this._showNewest}
|
||||
></ha-icon-button>
|
||||
</div>
|
||||
<ha-yaml-editor
|
||||
.hass=${this.hass}
|
||||
.value=${event.event}
|
||||
auto-update
|
||||
read-only
|
||||
></ha-yaml-editor>
|
||||
</ha-card>
|
||||
`;
|
||||
}
|
||||
|
||||
private _resolveViewedIndex(): number {
|
||||
if (this._viewedEventId === undefined) {
|
||||
return 0;
|
||||
}
|
||||
const found = this._events.findIndex((e) => e.id === this._viewedEventId);
|
||||
|
||||
// Fall back to the oldest available event when the viewed one has aged out.
|
||||
return found === -1 ? this._events.length - 1 : found;
|
||||
}
|
||||
|
||||
private _showOldest() {
|
||||
if (!this._events.length) {
|
||||
return;
|
||||
}
|
||||
this._viewedEventId = this._events[this._events.length - 1].id;
|
||||
}
|
||||
|
||||
private _showOlder() {
|
||||
if (!this._events.length) {
|
||||
return;
|
||||
}
|
||||
const next = Math.min(
|
||||
this._resolveViewedIndex() + 1,
|
||||
this._events.length - 1
|
||||
);
|
||||
this._viewedEventId = this._events[next].id;
|
||||
}
|
||||
|
||||
private _showNewest() {
|
||||
if (!this._events.length) {
|
||||
return;
|
||||
}
|
||||
this._viewedEventId = this._events[0].id;
|
||||
}
|
||||
|
||||
private _showNewer() {
|
||||
if (!this._events.length) {
|
||||
return;
|
||||
}
|
||||
const next = Math.max(this._resolveViewedIndex() - 1, 0);
|
||||
this._viewedEventId = this._events[next].id;
|
||||
}
|
||||
|
||||
private _valueChanged(ev: InputEvent) {
|
||||
private _valueChanged(ev: InputEvent): void {
|
||||
this._eventType = (ev.target as HaInput).value ?? "";
|
||||
this._error = undefined;
|
||||
}
|
||||
|
||||
private _filterChanged(ev: InputEvent) {
|
||||
private _filterChanged(ev: InputEvent): void {
|
||||
this._eventFilter = (ev.target as HaInput).value ?? "";
|
||||
}
|
||||
|
||||
@@ -295,7 +160,7 @@ class EventSubscribeCard extends LitElement {
|
||||
|
||||
const searchStr = this._eventFilter;
|
||||
|
||||
function visit(node: unknown) {
|
||||
function visit(node) {
|
||||
// Handle primitives directly
|
||||
if (node === null || typeof node !== "object") {
|
||||
return String(node).includes(searchStr);
|
||||
@@ -338,116 +203,55 @@ class EventSubscribeCard extends LitElement {
|
||||
return;
|
||||
}
|
||||
const tail =
|
||||
this._events.length >= MAX_BUFFERED_EVENTS
|
||||
? this._events.slice(0, MAX_BUFFERED_EVENTS - 1)
|
||||
this._events.length > 30
|
||||
? this._events.slice(0, 29)
|
||||
: this._events;
|
||||
const id = this._eventCount++;
|
||||
this._events = [
|
||||
{
|
||||
event,
|
||||
id,
|
||||
id: this._eventCount++,
|
||||
},
|
||||
...tail,
|
||||
];
|
||||
if (this._viewedEventId === undefined) {
|
||||
this._viewedEventId = id;
|
||||
}
|
||||
}, this._eventType);
|
||||
} catch (error) {
|
||||
} catch (error: any) {
|
||||
this._error = this.hass!.localize(
|
||||
"ui.panel.config.developer-tools.tabs.events.subscribe_failed",
|
||||
{
|
||||
error:
|
||||
error instanceof Error
|
||||
? error.message
|
||||
: this.hass!.localize(
|
||||
"ui.panel.config.developer-tools.tabs.events.unknown_error"
|
||||
),
|
||||
}
|
||||
{ error: error.message || "Unknown error" }
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _clearEvents() {
|
||||
private _clearEvents(): void {
|
||||
this._events = [];
|
||||
this._eventCount = 0;
|
||||
this._ignoredEventsCount = 0;
|
||||
this._error = undefined;
|
||||
this._viewedEventId = undefined;
|
||||
}
|
||||
|
||||
static styles = css`
|
||||
:host {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 0;
|
||||
}
|
||||
ha-input {
|
||||
margin-bottom: var(--ha-space-2);
|
||||
}
|
||||
.error-message {
|
||||
margin-top: var(--ha-space-2);
|
||||
}
|
||||
.event {
|
||||
border-top: 1px solid var(--divider-color);
|
||||
padding-top: var(--ha-space-2);
|
||||
padding-bottom: var(--ha-space-2);
|
||||
margin: var(--ha-space-4) 0;
|
||||
}
|
||||
.event:last-child {
|
||||
border-bottom: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
pre {
|
||||
font-family: var(--ha-font-family-code);
|
||||
}
|
||||
ha-card {
|
||||
margin-bottom: var(--ha-space-2);
|
||||
}
|
||||
.events-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 620px;
|
||||
padding: var(--ha-space-2);
|
||||
}
|
||||
:host([narrow]) .events-card {
|
||||
height: auto;
|
||||
min-height: 360px;
|
||||
}
|
||||
.events-toolbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--ha-space-2);
|
||||
}
|
||||
.empty-state {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: var(--ha-space-8);
|
||||
color: var(--primary-text-color);
|
||||
text-align: center;
|
||||
font-size: var(--ha-font-size-xl);
|
||||
line-height: var(--ha-line-height-normal);
|
||||
}
|
||||
.event-info {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
font-size: var(--ha-font-size-m);
|
||||
}
|
||||
.counter {
|
||||
color: var(--secondary-text-color);
|
||||
margin-left: var(--ha-space-2);
|
||||
}
|
||||
.buffer-info {
|
||||
color: var(--secondary-text-color);
|
||||
margin-left: var(--ha-space-1);
|
||||
vertical-align: middle;
|
||||
--mdc-icon-size: 16px;
|
||||
}
|
||||
.buffer-tooltip {
|
||||
white-space: pre-line;
|
||||
display: block;
|
||||
max-width: 320px;
|
||||
}
|
||||
ha-yaml-editor {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 0;
|
||||
flex: 1;
|
||||
margin-top: var(--ha-space-2);
|
||||
--code-mirror-height: 100%;
|
||||
margin-bottom: var(--ha-space-1);
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -623,36 +623,6 @@
|
||||
"between": "In range",
|
||||
"outside": "Outside range"
|
||||
}
|
||||
},
|
||||
"automation_behavior": {
|
||||
"trigger": {
|
||||
"options": {
|
||||
"any": {
|
||||
"label": "Each",
|
||||
"description": "Triggers once for every target that changes"
|
||||
},
|
||||
"first": {
|
||||
"label": "First",
|
||||
"description": "Triggers the first time any target changes"
|
||||
},
|
||||
"last": {
|
||||
"label": "All",
|
||||
"description": "Triggers once all targets have changed"
|
||||
}
|
||||
}
|
||||
},
|
||||
"condition": {
|
||||
"options": {
|
||||
"any": {
|
||||
"label": "Any",
|
||||
"description": "Is true if any target matches the desired state"
|
||||
},
|
||||
"all": {
|
||||
"label": "All",
|
||||
"description": "Is true if all targets match the desired state"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"logbook": {
|
||||
@@ -3781,7 +3751,7 @@
|
||||
"type": "Event type",
|
||||
"data": "Event data (YAML, optional)",
|
||||
"fire_event": "Fire event",
|
||||
"event_fired": "Event {name} fired at {time}",
|
||||
"event_fired": "Event {name} fired",
|
||||
"active_listeners": "Active listeners",
|
||||
"count_listeners": "({count} {count, plural,\n one {listener}\n other {listeners}\n})",
|
||||
"listen_to_events": "Listen to events",
|
||||
@@ -3795,15 +3765,7 @@
|
||||
"clear_events": "Clear events",
|
||||
"alert_event_type": "Event type is a mandatory field",
|
||||
"notification_event_fired": "Event {type} successfully fired!",
|
||||
"subscribe_failed": "Failed to subscribe to event: {error}",
|
||||
"unknown_error": "Unknown error",
|
||||
"oldest_event": "Oldest event",
|
||||
"older_event": "Older event",
|
||||
"newer_event": "Newer event",
|
||||
"newest_event": "Newest event",
|
||||
"waiting_for_events": "Waiting for events…",
|
||||
"subscribe_prompt": "Subscribe to an event type above to see events here.",
|
||||
"buffer_disclaimer": "Showing the latest {count} events. Older events are discarded as new ones arrive.\n\nTip: Subscribe to a more specific event type, or use the filter to narrow results."
|
||||
"subscribe_failed": "Failed to subscribe to event: {error}"
|
||||
},
|
||||
"actions": {
|
||||
"title": "Actions",
|
||||
@@ -5117,14 +5079,14 @@
|
||||
"entity": {
|
||||
"label": "Entity"
|
||||
},
|
||||
"helpers": {
|
||||
"label": "Helpers"
|
||||
},
|
||||
"time_location": {
|
||||
"label": "Time and location"
|
||||
},
|
||||
"generic": {
|
||||
"label": "Generic"
|
||||
},
|
||||
"custom_integrations": {
|
||||
"label": "Custom integrations"
|
||||
"other": {
|
||||
"label": "Other triggers"
|
||||
}
|
||||
},
|
||||
"type": {
|
||||
@@ -5392,14 +5354,14 @@
|
||||
"time_location": {
|
||||
"label": "Time and location"
|
||||
},
|
||||
"generic": {
|
||||
"label": "Generic"
|
||||
"helpers": {
|
||||
"label": "Helpers"
|
||||
},
|
||||
"other": {
|
||||
"label": "Other conditions"
|
||||
},
|
||||
"building_blocks": {
|
||||
"label": "Building blocks"
|
||||
},
|
||||
"custom_integrations": {
|
||||
"label": "Custom integrations"
|
||||
}
|
||||
},
|
||||
"type": {
|
||||
|
||||
Reference in New Issue
Block a user