mirror of
https://github.com/home-assistant/frontend.git
synced 2026-04-02 08:54:03 +00:00
Compare commits
1 Commits
fix-issue-
...
protocol-b
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
081a13b3d7 |
@@ -1,4 +1,4 @@
|
||||
FROM mcr.microsoft.com/devcontainers/python:3.14
|
||||
FROM mcr.microsoft.com/devcontainers/python:1-3.14
|
||||
|
||||
ENV \
|
||||
DEBIAN_FRONTEND=noninteractive \
|
||||
|
||||
@@ -100,6 +100,7 @@ class HaLandingPage extends LandingPageBaseElement {
|
||||
button-style
|
||||
native-name
|
||||
@value-changed=${this._languageChanged}
|
||||
inline-arrow
|
||||
></ha-language-picker>
|
||||
<ha-button
|
||||
appearance="plain"
|
||||
|
||||
29
package.json
29
package.json
@@ -37,15 +37,15 @@
|
||||
"@codemirror/view": "6.39.12",
|
||||
"@date-fns/tz": "1.4.1",
|
||||
"@egjs/hammerjs": "2.0.17",
|
||||
"@formatjs/intl-datetimeformat": "7.2.1",
|
||||
"@formatjs/intl-displaynames": "7.2.1",
|
||||
"@formatjs/intl-durationformat": "0.10.1",
|
||||
"@formatjs/intl-getcanonicallocales": "3.2.1",
|
||||
"@formatjs/intl-listformat": "8.2.1",
|
||||
"@formatjs/intl-locale": "5.2.1",
|
||||
"@formatjs/intl-numberformat": "9.2.2",
|
||||
"@formatjs/intl-pluralrules": "6.2.2",
|
||||
"@formatjs/intl-relativetimeformat": "12.2.2",
|
||||
"@formatjs/intl-datetimeformat": "7.2.0",
|
||||
"@formatjs/intl-displaynames": "7.2.0",
|
||||
"@formatjs/intl-durationformat": "0.10.0",
|
||||
"@formatjs/intl-getcanonicallocales": "3.2.0",
|
||||
"@formatjs/intl-listformat": "8.2.0",
|
||||
"@formatjs/intl-locale": "5.2.0",
|
||||
"@formatjs/intl-numberformat": "9.2.1",
|
||||
"@formatjs/intl-pluralrules": "6.2.1",
|
||||
"@formatjs/intl-relativetimeformat": "12.2.1",
|
||||
"@fullcalendar/core": "6.1.20",
|
||||
"@fullcalendar/daygrid": "6.1.20",
|
||||
"@fullcalendar/interaction": "6.1.20",
|
||||
@@ -71,6 +71,7 @@
|
||||
"@material/mwc-icon-button": "0.27.0",
|
||||
"@material/mwc-linear-progress": "0.27.0",
|
||||
"@material/mwc-list": "patch:@material/mwc-list@npm%3A0.27.0#~/.yarn/patches/@material-mwc-list-npm-0.27.0-5344fc9de4.patch",
|
||||
"@material/mwc-menu": "0.27.0",
|
||||
"@material/mwc-radio": "0.27.0",
|
||||
"@material/mwc-select": "0.27.0",
|
||||
"@material/mwc-snackbar": "0.27.0",
|
||||
@@ -111,7 +112,7 @@
|
||||
"hls.js": "1.6.15",
|
||||
"home-assistant-js-websocket": "9.6.0",
|
||||
"idb-keyval": "6.2.2",
|
||||
"intl-messageformat": "11.1.2",
|
||||
"intl-messageformat": "11.1.1",
|
||||
"js-yaml": "4.1.1",
|
||||
"leaflet": "1.9.4",
|
||||
"leaflet-draw": "patch:leaflet-draw@npm%3A1.0.4#./.yarn/patches/leaflet-draw-npm-1.0.4-0ca0ebcf65.patch",
|
||||
@@ -145,10 +146,10 @@
|
||||
"xss": "1.0.15"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "7.29.0",
|
||||
"@babel/core": "7.28.6",
|
||||
"@babel/helper-define-polyfill-provider": "0.6.6",
|
||||
"@babel/plugin-transform-runtime": "7.29.0",
|
||||
"@babel/preset-env": "7.29.0",
|
||||
"@babel/plugin-transform-runtime": "7.28.5",
|
||||
"@babel/preset-env": "7.28.6",
|
||||
"@bundle-stats/plugin-webpack-filter": "4.21.9",
|
||||
"@lokalise/node-api": "15.6.1",
|
||||
"@octokit/auth-oauth-device": "8.0.3",
|
||||
@@ -228,7 +229,7 @@
|
||||
"clean-css": "5.3.3",
|
||||
"@lit/reactive-element": "2.1.2",
|
||||
"@fullcalendar/daygrid": "6.1.20",
|
||||
"globals": "17.3.0",
|
||||
"globals": "17.2.0",
|
||||
"tslib": "2.8.1",
|
||||
"@material/mwc-list@^0.27.0": "patch:@material/mwc-list@npm%3A0.27.0#~/.yarn/patches/@material-mwc-list-npm-0.27.0-5344fc9de4.patch",
|
||||
"glob@^10.2.2": "^10.5.0"
|
||||
|
||||
@@ -16,12 +16,6 @@ if [[ -n "$DEVCONTAINER" ]]; then
|
||||
nvm install --reinstall-packages-from="$nodeCurrent" --default
|
||||
nvm uninstall "$nodeCurrent"
|
||||
fi
|
||||
|
||||
# install yarn if not already available
|
||||
if ! command -v yarn &> /dev/null; then
|
||||
npm install -g corepack
|
||||
yes | yarn
|
||||
fi
|
||||
fi
|
||||
|
||||
if ! command -v yarn &> /dev/null; then
|
||||
|
||||
@@ -194,6 +194,7 @@ export class HaAuthorize extends litLocalizeLiteMixin(LitElement) {
|
||||
button-style
|
||||
native-name
|
||||
@value-changed=${this._languageChanged}
|
||||
inline-arrow
|
||||
></ha-language-picker>
|
||||
<ha-button
|
||||
appearance="plain"
|
||||
|
||||
@@ -19,7 +19,6 @@ import { styleMap } from "lit/directives/style-map";
|
||||
import { ensureArray } from "../../common/array/ensure-array";
|
||||
import { getAllGraphColors } from "../../common/color/colors";
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
import type { HASSDomEvent } from "../../common/dom/fire_event";
|
||||
import { listenMediaQuery } from "../../common/dom/media_query";
|
||||
import { themesContext } from "../../data/context";
|
||||
import type { Themes } from "../../data/ws-themes";
|
||||
@@ -28,7 +27,6 @@ import type { HomeAssistant } from "../../types";
|
||||
import { isMac } from "../../util/is_mac";
|
||||
import "../chips/ha-assist-chip";
|
||||
import "../ha-icon-button";
|
||||
import { afterNextRender } from "../../common/util/render-status";
|
||||
import { filterXSS } from "../../common/util/xss";
|
||||
import { formatTimeLabel } from "./axis-label";
|
||||
import { downSampleLineData } from "./down-sample";
|
||||
@@ -94,18 +92,10 @@ export class HaChartBase extends LitElement {
|
||||
|
||||
private _resizeAnimationDuration?: number;
|
||||
|
||||
private _suspendResize = false;
|
||||
|
||||
private _layoutTransitionActive = false;
|
||||
|
||||
// @ts-ignore
|
||||
private _resizeController = new ResizeController(this, {
|
||||
callback: () => {
|
||||
if (this.chart) {
|
||||
if (this._suspendResize) {
|
||||
this._shouldResizeChart = true;
|
||||
return;
|
||||
}
|
||||
if (!this.chart.getZr().animation.isFinished()) {
|
||||
this._shouldResizeChart = true;
|
||||
} else {
|
||||
@@ -123,11 +113,8 @@ export class HaChartBase extends LitElement {
|
||||
|
||||
private _originalZrFlush?: () => void;
|
||||
|
||||
private _pendingSetup = false;
|
||||
|
||||
public disconnectedCallback() {
|
||||
super.disconnectedCallback();
|
||||
this._pendingSetup = false;
|
||||
while (this._listeners.length) {
|
||||
this._listeners.pop()!();
|
||||
}
|
||||
@@ -139,13 +126,7 @@ export class HaChartBase extends LitElement {
|
||||
public connectedCallback() {
|
||||
super.connectedCallback();
|
||||
if (this.hasUpdated) {
|
||||
this._pendingSetup = true;
|
||||
afterNextRender(() => {
|
||||
if (this.isConnected && this._pendingSetup) {
|
||||
this._pendingSetup = false;
|
||||
this._setupChart();
|
||||
}
|
||||
});
|
||||
this._setupChart();
|
||||
}
|
||||
|
||||
this._listeners.push(
|
||||
@@ -200,26 +181,6 @@ export class HaChartBase extends LitElement {
|
||||
() => window.removeEventListener("keyup", handleKeyUp)
|
||||
);
|
||||
}
|
||||
|
||||
const handleLayoutTransition: EventListener = (ev) => {
|
||||
const event = ev as HASSDomEvent<HASSDomEvents["hass-layout-transition"]>;
|
||||
this._layoutTransitionActive = Boolean(event.detail?.active);
|
||||
this.toggleAttribute(
|
||||
"layout-transition-active",
|
||||
this._layoutTransitionActive
|
||||
);
|
||||
this._suspendResize = this._layoutTransitionActive;
|
||||
if (!this._suspendResize) {
|
||||
this._resizeChartIfNeeded();
|
||||
}
|
||||
};
|
||||
window.addEventListener("hass-layout-transition", handleLayoutTransition);
|
||||
this._listeners.push(() =>
|
||||
window.removeEventListener(
|
||||
"hass-layout-transition",
|
||||
handleLayoutTransition
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
protected firstUpdated() {
|
||||
@@ -1027,29 +988,19 @@ export class HaChartBase extends LitElement {
|
||||
}
|
||||
|
||||
private _handleChartRenderFinished = () => {
|
||||
this._resizeChartIfNeeded();
|
||||
if (this._shouldResizeChart) {
|
||||
this.chart?.resize({
|
||||
animation:
|
||||
this._reducedMotion ||
|
||||
typeof this._resizeAnimationDuration !== "number"
|
||||
? undefined
|
||||
: { duration: this._resizeAnimationDuration },
|
||||
});
|
||||
this._shouldResizeChart = false;
|
||||
this._resizeAnimationDuration = undefined;
|
||||
}
|
||||
};
|
||||
|
||||
private _resizeChartIfNeeded() {
|
||||
if (!this.chart || !this._shouldResizeChart) {
|
||||
return;
|
||||
}
|
||||
if (this._suspendResize) {
|
||||
return;
|
||||
}
|
||||
if (!this.chart.getZr().animation.isFinished()) {
|
||||
return;
|
||||
}
|
||||
this.chart.resize({
|
||||
animation:
|
||||
this._reducedMotion || typeof this._resizeAnimationDuration !== "number"
|
||||
? undefined
|
||||
: { duration: this._resizeAnimationDuration },
|
||||
});
|
||||
this._shouldResizeChart = false;
|
||||
this._resizeAnimationDuration = undefined;
|
||||
}
|
||||
|
||||
private _compareCustomLegendOptions(
|
||||
oldOptions: ECOption | undefined,
|
||||
newOptions: ECOption | undefined
|
||||
@@ -1071,18 +1022,11 @@ export class HaChartBase extends LitElement {
|
||||
display: block;
|
||||
position: relative;
|
||||
letter-spacing: normal;
|
||||
overflow: visible;
|
||||
}
|
||||
:host([layout-transition-active]),
|
||||
:host([layout-transition-active]) .container,
|
||||
:host([layout-transition-active]) .chart-container {
|
||||
overflow: hidden;
|
||||
}
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
overflow: visible;
|
||||
}
|
||||
.container.has-height {
|
||||
max-height: var(--chart-max-height, 350px);
|
||||
@@ -1090,7 +1034,6 @@ export class HaChartBase extends LitElement {
|
||||
.chart-container {
|
||||
width: 100%;
|
||||
max-height: var(--chart-max-height, 350px);
|
||||
overflow: visible;
|
||||
}
|
||||
.has-height .chart-container {
|
||||
flex: 1;
|
||||
|
||||
@@ -48,6 +48,7 @@ export class HaAnsiToHtml extends LitElement {
|
||||
|
||||
static styles = css`
|
||||
pre {
|
||||
overflow-x: auto;
|
||||
margin: 0;
|
||||
}
|
||||
pre.wrap {
|
||||
|
||||
@@ -2,12 +2,14 @@ import type { PropertyValueMap } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
import { stopPropagation } from "../common/dom/stop_propagation";
|
||||
import { formatLanguageCode } from "../common/language/format_language";
|
||||
import type { AssistPipeline } from "../data/assist_pipeline";
|
||||
import { listAssistPipelines } from "../data/assist_pipeline";
|
||||
import type { HomeAssistant } from "../types";
|
||||
import "./ha-list-item";
|
||||
import "./ha-select";
|
||||
import type { HaSelectOption } from "./ha-select";
|
||||
import type { HaSelect } from "./ha-select";
|
||||
|
||||
const PREFERRED = "preferred";
|
||||
const LAST_USED = "last_used";
|
||||
@@ -39,31 +41,6 @@ export class HaAssistPipelinePicker extends LitElement {
|
||||
return nothing;
|
||||
}
|
||||
const value = this.value ?? this._default;
|
||||
const options: HaSelectOption[] = [
|
||||
{
|
||||
value: PREFERRED,
|
||||
label: this.hass.localize("ui.components.pipeline-picker.preferred", {
|
||||
preferred: this._pipelines.find(
|
||||
(pipeline) => pipeline.id === this._preferredPipeline
|
||||
)?.name,
|
||||
}),
|
||||
},
|
||||
];
|
||||
|
||||
if (this.includeLastUsed) {
|
||||
options.unshift({
|
||||
value: LAST_USED,
|
||||
label: this.hass.localize("ui.components.pipeline-picker.last_used"),
|
||||
});
|
||||
}
|
||||
|
||||
options.push(
|
||||
...this._pipelines.map((pipeline) => ({
|
||||
value: pipeline.id,
|
||||
label: `${pipeline.name} (${formatLanguageCode(pipeline.language, this.hass.locale)})`,
|
||||
}))
|
||||
);
|
||||
|
||||
return html`
|
||||
<ha-select
|
||||
.label=${this.label ||
|
||||
@@ -72,8 +49,33 @@ export class HaAssistPipelinePicker extends LitElement {
|
||||
.required=${this.required}
|
||||
.disabled=${this.disabled}
|
||||
@selected=${this._changed}
|
||||
.options=${options}
|
||||
@closed=${stopPropagation}
|
||||
fixedMenuPosition
|
||||
naturalMenuWidth
|
||||
>
|
||||
${this.includeLastUsed
|
||||
? html`
|
||||
<ha-list-item .value=${LAST_USED}>
|
||||
${this.hass!.localize(
|
||||
"ui.components.pipeline-picker.last_used"
|
||||
)}
|
||||
</ha-list-item>
|
||||
`
|
||||
: null}
|
||||
<ha-list-item .value=${PREFERRED}>
|
||||
${this.hass!.localize("ui.components.pipeline-picker.preferred", {
|
||||
preferred: this._pipelines.find(
|
||||
(pipeline) => pipeline.id === this._preferredPipeline
|
||||
)?.name,
|
||||
})}
|
||||
</ha-list-item>
|
||||
${this._pipelines.map(
|
||||
(pipeline) =>
|
||||
html`<ha-list-item .value=${pipeline.id}>
|
||||
${pipeline.name}
|
||||
(${formatLanguageCode(pipeline.language, this.hass.locale)})
|
||||
</ha-list-item>`
|
||||
)}
|
||||
</ha-select>
|
||||
`;
|
||||
}
|
||||
@@ -94,17 +96,17 @@ export class HaAssistPipelinePicker extends LitElement {
|
||||
}
|
||||
`;
|
||||
|
||||
private _changed(ev: CustomEvent<{ value: string }>): void {
|
||||
const value = ev.detail.value;
|
||||
private _changed(ev): void {
|
||||
const target = ev.target as HaSelect;
|
||||
if (
|
||||
!this.hass ||
|
||||
value === "" ||
|
||||
value === this.value ||
|
||||
(this.value === undefined && value === this._default)
|
||||
target.value === "" ||
|
||||
target.value === this.value ||
|
||||
(this.value === undefined && target.value === this._default)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
this.value = value === this._default ? undefined : value;
|
||||
this.value = target.value === this._default ? undefined : target.value;
|
||||
fireEvent(this, "value-changed", { value: this.value });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,8 +4,10 @@ import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { ifDefined } from "lit/directives/if-defined";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
import { stopPropagation } from "../common/dom/stop_propagation";
|
||||
import "./ha-icon-button";
|
||||
import "./ha-input-helper-text";
|
||||
import "./ha-list-item";
|
||||
import "./ha-select";
|
||||
import "./ha-textfield";
|
||||
import type { HaTextField } from "./ha-textfield";
|
||||
@@ -258,10 +260,14 @@ export class HaBaseTimeInput extends LitElement {
|
||||
.required=${this.required}
|
||||
.value=${this.amPm}
|
||||
.disabled=${this.disabled}
|
||||
.name=${"amPm"}
|
||||
name="amPm"
|
||||
naturalMenuWidth
|
||||
fixedMenuPosition
|
||||
@selected=${this._valueChanged}
|
||||
.options=${["AM", "PM"]}
|
||||
@closed=${stopPropagation}
|
||||
>
|
||||
<ha-list-item value="AM">AM</ha-list-item>
|
||||
<ha-list-item value="PM">PM</ha-list-item>
|
||||
</ha-select>`}
|
||||
</div>
|
||||
${this.helper
|
||||
@@ -276,12 +282,10 @@ export class HaBaseTimeInput extends LitElement {
|
||||
fireEvent(this, "value-changed");
|
||||
}
|
||||
|
||||
private _valueChanged(ev: InputEvent | CustomEvent<{ value: string }>): void {
|
||||
private _valueChanged(ev: InputEvent) {
|
||||
const textField = ev.currentTarget as HaTextField;
|
||||
this[textField.name] =
|
||||
textField.name === "amPm"
|
||||
? (ev as CustomEvent<{ value: string }>).detail.value
|
||||
: Number(textField.value);
|
||||
textField.name === "amPm" ? textField.value : Number(textField.value);
|
||||
const value: TimeChangedEvent = {
|
||||
hours: this.hours,
|
||||
minutes: this.minutes,
|
||||
@@ -362,6 +366,10 @@ export class HaBaseTimeInput extends LitElement {
|
||||
ha-textfield:last-child {
|
||||
--text-field-border-top-right-radius: var(--mdc-shape-medium);
|
||||
}
|
||||
ha-select {
|
||||
--mdc-shape-small: 0;
|
||||
width: 85px;
|
||||
}
|
||||
:host([clearable]) .mdc-select__anchor {
|
||||
padding-inline-end: var(--select-selected-text-padding-end, 12px);
|
||||
}
|
||||
|
||||
@@ -2,10 +2,12 @@ import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
import { stopPropagation } from "../common/dom/stop_propagation";
|
||||
import { stringCompare } from "../common/string/compare";
|
||||
import type { Blueprint, BlueprintDomain, Blueprints } from "../data/blueprint";
|
||||
import { fetchBlueprints } from "../data/blueprint";
|
||||
import type { HomeAssistant } from "../types";
|
||||
import "./ha-list-item";
|
||||
import "./ha-select";
|
||||
|
||||
@customElement("ha-blueprint-picker")
|
||||
@@ -53,16 +55,20 @@ class HaBluePrintPicker extends LitElement {
|
||||
<ha-select
|
||||
.label=${this.label ||
|
||||
this.hass.localize("ui.components.blueprint-picker.select_blueprint")}
|
||||
fixedMenuPosition
|
||||
naturalMenuWidth
|
||||
.value=${this.value}
|
||||
.disabled=${this.disabled}
|
||||
@selected=${this._blueprintChanged}
|
||||
.options=${this._processedBlueprints(this.blueprints).map(
|
||||
(blueprint) => ({
|
||||
value: blueprint.path,
|
||||
label: blueprint.name,
|
||||
})
|
||||
)}
|
||||
@closed=${stopPropagation}
|
||||
>
|
||||
${this._processedBlueprints(this.blueprints).map(
|
||||
(blueprint) => html`
|
||||
<ha-list-item .value=${blueprint.path}>
|
||||
${blueprint.name}
|
||||
</ha-list-item>
|
||||
`
|
||||
)}
|
||||
</ha-select>
|
||||
`;
|
||||
}
|
||||
@@ -76,8 +82,8 @@ class HaBluePrintPicker extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
private _blueprintChanged(ev: CustomEvent<{ value: string }>) {
|
||||
const newValue = ev.detail.value;
|
||||
private _blueprintChanged(ev) {
|
||||
const newValue = ev.target.value;
|
||||
|
||||
if (newValue !== this.value) {
|
||||
this.value = newValue;
|
||||
|
||||
@@ -3,6 +3,7 @@ import type { PropertyValues } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
import { stopPropagation } from "../common/dom/stop_propagation";
|
||||
import { debounce } from "../common/util/debounce";
|
||||
import type { ConfigEntry, SubEntry } from "../data/config_entries";
|
||||
import { getConfigEntry, getSubEntries } from "../data/config_entries";
|
||||
@@ -13,8 +14,9 @@ import { fetchIntegrationManifest } from "../data/integration";
|
||||
import { showOptionsFlowDialog } from "../dialogs/config-flow/show-dialog-options-flow";
|
||||
import { showSubConfigFlowDialog } from "../dialogs/config-flow/show-dialog-sub-config-flow";
|
||||
import type { HomeAssistant } from "../types";
|
||||
import "./ha-list-item";
|
||||
import "./ha-select";
|
||||
import type { HaSelectOption } from "./ha-select";
|
||||
import type { HaSelect } from "./ha-select";
|
||||
|
||||
const NONE = "__NONE_OPTION__";
|
||||
|
||||
@@ -71,35 +73,37 @@ export class HaConversationAgentPicker extends LitElement {
|
||||
value = NONE;
|
||||
}
|
||||
|
||||
const options: HaSelectOption[] = this._agents.map((agent) => ({
|
||||
value: agent.id,
|
||||
label: agent.name,
|
||||
disabled:
|
||||
agent.supported_languages !== "*" &&
|
||||
agent.supported_languages.length === 0,
|
||||
}));
|
||||
|
||||
if (!this.required) {
|
||||
options.unshift({
|
||||
value: NONE,
|
||||
label: this.hass.localize(
|
||||
"ui.components.conversation-agent-picker.none"
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
return html`
|
||||
<ha-select
|
||||
.label=${this.label ||
|
||||
this.hass!.localize(
|
||||
"ui.components.conversation-agent-picker.conversation_agent"
|
||||
"ui.components.coversation-agent-picker.conversation_agent"
|
||||
)}
|
||||
.value=${value}
|
||||
.required=${this.required}
|
||||
.disabled=${this.disabled}
|
||||
@selected=${this._changed}
|
||||
.options=${options}
|
||||
></ha-select
|
||||
@closed=${stopPropagation}
|
||||
fixedMenuPosition
|
||||
naturalMenuWidth
|
||||
>
|
||||
${!this.required
|
||||
? html`<ha-list-item .value=${NONE}>
|
||||
${this.hass!.localize(
|
||||
"ui.components.coversation-agent-picker.none"
|
||||
)}
|
||||
</ha-list-item>`
|
||||
: nothing}
|
||||
${this._agents.map(
|
||||
(agent) =>
|
||||
html`<ha-list-item
|
||||
.value=${agent.id}
|
||||
.disabled=${agent.supported_languages !== "*" &&
|
||||
agent.supported_languages.length === 0}
|
||||
>
|
||||
${agent.name}
|
||||
</ha-list-item>`
|
||||
)}</ha-select
|
||||
>${(this._subConfigEntry &&
|
||||
this._configEntry?.supported_subentry_types[
|
||||
this._subConfigEntry.subentry_type
|
||||
@@ -234,17 +238,17 @@ export class HaConversationAgentPicker extends LitElement {
|
||||
}
|
||||
`;
|
||||
|
||||
private _changed(ev: CustomEvent<{ value: string }>): void {
|
||||
const value = ev.detail.value;
|
||||
private _changed(ev): void {
|
||||
const target = ev.target as HaSelect;
|
||||
if (
|
||||
!this.hass ||
|
||||
value === "" ||
|
||||
value === this.value ||
|
||||
(this.value === undefined && value === NONE)
|
||||
target.value === "" ||
|
||||
target.value === this.value ||
|
||||
(this.value === undefined && target.value === NONE)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
this.value = value === NONE ? undefined : value;
|
||||
this.value = target.value === NONE ? undefined : target.value;
|
||||
fireEvent(this, "value-changed", { value: this.value });
|
||||
fireEvent(this, "supported-languages-changed", {
|
||||
value: this._agents!.find((agent) => agent.id === this.value)
|
||||
|
||||
@@ -76,18 +76,6 @@ export class HaDialog extends DialogBase {
|
||||
var(--divider-color)
|
||||
);
|
||||
z-index: var(--dialog-z-index, 8);
|
||||
--mdc-dialog-box-shadow: var(--dialog-box-shadow, none);
|
||||
--mdc-typography-headline6-font-weight: var(--ha-font-weight-normal);
|
||||
--mdc-typography-headline6-font-size: 1.574rem;
|
||||
}
|
||||
.mdc-dialog::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
pointer-events: none;
|
||||
-webkit-backdrop-filter: var(
|
||||
--ha-dialog-scrim-backdrop-filter,
|
||||
var(--dialog-backdrop-filter, none)
|
||||
@@ -96,6 +84,9 @@ export class HaDialog extends DialogBase {
|
||||
--ha-dialog-scrim-backdrop-filter,
|
||||
var(--dialog-backdrop-filter, none)
|
||||
);
|
||||
--mdc-dialog-box-shadow: var(--dialog-box-shadow, none);
|
||||
--mdc-typography-headline6-font-weight: var(--ha-font-weight-normal);
|
||||
--mdc-typography-headline6-font-size: 1.574rem;
|
||||
}
|
||||
.mdc-dialog .mdc-dialog__scrim {
|
||||
background-color: var(--mdc-dialog-scrim-color, none);
|
||||
|
||||
@@ -4,18 +4,6 @@ import type { PropertyValues } from "lit";
|
||||
import { css } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
import type { HASSDomEvent } from "../common/dom/fire_event";
|
||||
|
||||
declare global {
|
||||
interface HASSDomEvents {
|
||||
"hass-layout-transition": { active: boolean; reason?: string };
|
||||
}
|
||||
interface HTMLElementEventMap {
|
||||
"hass-layout-transition": HASSDomEvent<
|
||||
HASSDomEvents["hass-layout-transition"]
|
||||
>;
|
||||
}
|
||||
}
|
||||
|
||||
const blockingElements = (document as any).$blockingElements;
|
||||
|
||||
@@ -27,30 +15,6 @@ export class HaDrawer extends DrawerBase {
|
||||
|
||||
private _rtlStyle?: HTMLElement;
|
||||
|
||||
private _sidebarTransitionActive = false;
|
||||
|
||||
private _handleDrawerTransitionStart = (ev: TransitionEvent) => {
|
||||
if (ev.propertyName !== "width" || this._sidebarTransitionActive) {
|
||||
return;
|
||||
}
|
||||
this._sidebarTransitionActive = true;
|
||||
fireEvent(window, "hass-layout-transition", {
|
||||
active: true,
|
||||
reason: "sidebar",
|
||||
});
|
||||
};
|
||||
|
||||
private _handleDrawerTransitionEnd = (ev: TransitionEvent) => {
|
||||
if (ev.propertyName !== "width" || !this._sidebarTransitionActive) {
|
||||
return;
|
||||
}
|
||||
this._sidebarTransitionActive = false;
|
||||
fireEvent(window, "hass-layout-transition", {
|
||||
active: false,
|
||||
reason: "sidebar",
|
||||
});
|
||||
};
|
||||
|
||||
protected createAdapter() {
|
||||
return {
|
||||
...super.createAdapter(),
|
||||
@@ -99,38 +63,6 @@ export class HaDrawer extends DrawerBase {
|
||||
}
|
||||
}
|
||||
|
||||
protected firstUpdated() {
|
||||
super.firstUpdated();
|
||||
this.mdcRoot?.addEventListener(
|
||||
"transitionstart",
|
||||
this._handleDrawerTransitionStart
|
||||
);
|
||||
this.mdcRoot?.addEventListener(
|
||||
"transitionend",
|
||||
this._handleDrawerTransitionEnd
|
||||
);
|
||||
this.mdcRoot?.addEventListener(
|
||||
"transitioncancel",
|
||||
this._handleDrawerTransitionEnd
|
||||
);
|
||||
}
|
||||
|
||||
public disconnectedCallback() {
|
||||
super.disconnectedCallback();
|
||||
this.mdcRoot?.removeEventListener(
|
||||
"transitionstart",
|
||||
this._handleDrawerTransitionStart
|
||||
);
|
||||
this.mdcRoot?.removeEventListener(
|
||||
"transitionend",
|
||||
this._handleDrawerTransitionEnd
|
||||
);
|
||||
this.mdcRoot?.removeEventListener(
|
||||
"transitioncancel",
|
||||
this._handleDrawerTransitionEnd
|
||||
);
|
||||
}
|
||||
|
||||
private async _setupSwipe() {
|
||||
const hammer = await import("../resources/hammer");
|
||||
this._mc = new hammer.Manager(document, {
|
||||
@@ -158,16 +90,6 @@ export class HaDrawer extends DrawerBase {
|
||||
border-color: var(--divider-color, rgba(0, 0, 0, 0.12));
|
||||
inset-inline-start: 0 !important;
|
||||
inset-inline-end: initial !important;
|
||||
transition-property: transform, width;
|
||||
transition-duration:
|
||||
var(--mdc-drawer-transition-duration, 0.2s),
|
||||
var(--ha-animation-duration-normal);
|
||||
transition-timing-function:
|
||||
var(
|
||||
--mdc-drawer-transition-timing-function,
|
||||
cubic-bezier(0.4, 0, 0.2, 1)
|
||||
),
|
||||
ease;
|
||||
}
|
||||
.mdc-drawer.mdc-drawer--modal.mdc-drawer--open {
|
||||
z-index: 200;
|
||||
@@ -181,15 +103,6 @@ export class HaDrawer extends DrawerBase {
|
||||
direction: var(--direction);
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
transition:
|
||||
padding-left var(--ha-animation-duration-normal) ease,
|
||||
padding-inline-start var(--ha-animation-duration-normal) ease;
|
||||
}
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.mdc-drawer,
|
||||
.mdc-drawer-app-content {
|
||||
transition: none;
|
||||
}
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
@@ -37,7 +37,6 @@ export class HaDropdownItem extends DropdownItem {
|
||||
|
||||
#check {
|
||||
visibility: visible;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
#icon ::slotted(*) {
|
||||
|
||||
@@ -107,6 +107,9 @@ export class HaLanguagePicker extends LitElement {
|
||||
|
||||
@property({ attribute: "no-sort", type: Boolean }) public noSort = false;
|
||||
|
||||
@property({ attribute: "inline-arrow", type: Boolean })
|
||||
public inlineArrow = false;
|
||||
|
||||
@state() _defaultLanguages: string[] = [];
|
||||
|
||||
@query("ha-generic-picker", true) public genericPicker!: HaGenericPicker;
|
||||
|
||||
45
src/components/ha-menu.ts
Normal file
45
src/components/ha-menu.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import { MenuBase } from "@material/mwc-menu/mwc-menu-base";
|
||||
import { styles } from "@material/mwc-menu/mwc-menu.css";
|
||||
import { html } from "lit";
|
||||
import { customElement } from "lit/decorators";
|
||||
import { classMap } from "lit/directives/class-map";
|
||||
import "./ha-list";
|
||||
|
||||
@customElement("ha-menu")
|
||||
export class HaMenu extends MenuBase {
|
||||
protected get listElement() {
|
||||
if (!this.listElement_) {
|
||||
this.listElement_ = this.renderRoot.querySelector("ha-list");
|
||||
return this.listElement_;
|
||||
}
|
||||
|
||||
return this.listElement_;
|
||||
}
|
||||
|
||||
protected renderList() {
|
||||
const itemRoles = this.innerRole === "menu" ? "menuitem" : "option";
|
||||
const classes = this.renderListClasses();
|
||||
|
||||
return html`<ha-list
|
||||
rootTabbable
|
||||
.innerAriaLabel=${this.innerAriaLabel}
|
||||
.innerRole=${this.innerRole}
|
||||
.multi=${this.multi}
|
||||
class=${classMap(classes)}
|
||||
.itemRoles=${itemRoles}
|
||||
.wrapFocus=${this.wrapFocus}
|
||||
.activatable=${this.activatable}
|
||||
@action=${this.onAction}
|
||||
>
|
||||
<slot></slot>
|
||||
</ha-list>`;
|
||||
}
|
||||
|
||||
static styles = styles;
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ha-menu": HaMenu;
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import { customElement, property, state } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { isComponentLoaded } from "../common/config/is_component_loaded";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
import { stopPropagation } from "../common/dom/stop_propagation";
|
||||
import { caseInsensitiveStringCompare } from "../common/string/compare";
|
||||
import type { SupervisorMounts } from "../data/supervisor/mounts";
|
||||
import {
|
||||
@@ -16,7 +17,7 @@ import type { HomeAssistant } from "../types";
|
||||
import "./ha-alert";
|
||||
import "./ha-list-item";
|
||||
import "./ha-select";
|
||||
import type { HaSelectOption } from "./ha-select";
|
||||
import type { HaSelect } from "./ha-select";
|
||||
|
||||
const _BACKUP_DATA_DISK_ = "/backup";
|
||||
|
||||
@@ -51,54 +52,60 @@ class HaMountPicker extends LitElement {
|
||||
if (!this._mounts) {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
const options: HaSelectOption[] = this._filterMounts(
|
||||
this._mounts,
|
||||
this.usage
|
||||
).map((mount) => ({
|
||||
value: mount.name,
|
||||
label: mount.name,
|
||||
secondary: `${mount.server}${mount.port ? `:${mount.port}` : ""}${
|
||||
mount.type === SupervisorMountType.NFS ? mount.path : `:${mount.share}`
|
||||
}`,
|
||||
iconPath:
|
||||
mount.usage === SupervisorMountUsage.MEDIA
|
||||
? mdiPlayBox
|
||||
: mount.usage === SupervisorMountUsage.SHARE
|
||||
? mdiFolder
|
||||
: mdiBackupRestore,
|
||||
}));
|
||||
|
||||
if (this.usage === SupervisorMountUsage.BACKUP) {
|
||||
const dataDiskOption = {
|
||||
value: _BACKUP_DATA_DISK_,
|
||||
iconPath: mdiHarddisk,
|
||||
label:
|
||||
this.hass.localize("ui.components.mount-picker.use_datadisk") ||
|
||||
"Use data disk for backup",
|
||||
};
|
||||
if (
|
||||
!this._mounts.default_backup_mount ||
|
||||
this._mounts.default_backup_mount === _BACKUP_DATA_DISK_
|
||||
) {
|
||||
options.unshift(dataDiskOption);
|
||||
} else {
|
||||
options.push(dataDiskOption);
|
||||
}
|
||||
}
|
||||
|
||||
const dataDiskOption = html`<ha-list-item
|
||||
graphic="icon"
|
||||
.value=${_BACKUP_DATA_DISK_}
|
||||
>
|
||||
<span>
|
||||
${this.hass.localize("ui.components.mount-picker.use_datadisk") ||
|
||||
"Use data disk for backup"}
|
||||
</span>
|
||||
<ha-svg-icon slot="graphic" .path=${mdiHarddisk}></ha-svg-icon>
|
||||
</ha-list-item>`;
|
||||
return html`
|
||||
<ha-select
|
||||
.label=${this.label === undefined && this.hass
|
||||
? this.hass.localize("ui.components.mount-picker.mount")
|
||||
: this.label}
|
||||
.value=${this.value}
|
||||
.value=${this._value}
|
||||
.required=${this.required}
|
||||
.disabled=${this.disabled}
|
||||
.helper=${this.helper}
|
||||
@selected=${this._mountChanged}
|
||||
.options=${options}
|
||||
@closed=${stopPropagation}
|
||||
fixedMenuPosition
|
||||
naturalMenuWidth
|
||||
>
|
||||
${this.usage === SupervisorMountUsage.BACKUP &&
|
||||
(!this._mounts.default_backup_mount ||
|
||||
this._mounts.default_backup_mount === _BACKUP_DATA_DISK_)
|
||||
? dataDiskOption
|
||||
: nothing}
|
||||
${this._filterMounts(this._mounts, this.usage).map(
|
||||
(mount) =>
|
||||
html`<ha-list-item twoline graphic="icon" .value=${mount.name}>
|
||||
<span>${mount.name}</span>
|
||||
<span slot="secondary"
|
||||
>${mount.server}${mount.port
|
||||
? `:${mount.port}`
|
||||
: nothing}${mount.type === SupervisorMountType.NFS
|
||||
? mount.path
|
||||
: `:${mount.share}`}</span
|
||||
>
|
||||
<ha-svg-icon
|
||||
slot="graphic"
|
||||
.path=${mount.usage === SupervisorMountUsage.MEDIA
|
||||
? mdiPlayBox
|
||||
: mount.usage === SupervisorMountUsage.SHARE
|
||||
? mdiFolder
|
||||
: mdiBackupRestore}
|
||||
></ha-svg-icon>
|
||||
</ha-list-item>`
|
||||
)}
|
||||
${this.usage === SupervisorMountUsage.BACKUP &&
|
||||
this._mounts.default_backup_mount
|
||||
? dataDiskOption
|
||||
: nothing}
|
||||
</ha-select>
|
||||
`;
|
||||
}
|
||||
@@ -146,10 +153,16 @@ class HaMountPicker extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
private _mountChanged(ev: CustomEvent<{ value: string }>) {
|
||||
const newValue = ev.detail.value;
|
||||
private get _value() {
|
||||
return this.value || "";
|
||||
}
|
||||
|
||||
if (newValue !== this.value) {
|
||||
private _mountChanged(ev: Event) {
|
||||
ev.stopPropagation();
|
||||
const target = ev.target as HaSelect;
|
||||
const newValue = target.value;
|
||||
|
||||
if (newValue !== this._value) {
|
||||
this._setValue(newValue);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -262,7 +262,12 @@ export class HaNavigationPicker extends LitElement {
|
||||
|
||||
const viewConfigs = await Promise.all(
|
||||
lovelacePanels.map((panel) =>
|
||||
fetchConfig(this.hass!.connection, panel.url_path, true)
|
||||
fetchConfig(
|
||||
this.hass!.connection,
|
||||
// path should be null to fetch default lovelace panel
|
||||
panel.url_path === "lovelace" ? null : panel.url_path,
|
||||
true
|
||||
)
|
||||
.then((config) => [panel.id, config] as [string, typeof config])
|
||||
.catch((_) => [panel.id, undefined] as [string, undefined])
|
||||
)
|
||||
|
||||
@@ -1,209 +1,187 @@
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { ifDefined } from "lit/directives/if-defined";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
import "./ha-dropdown";
|
||||
import "./ha-dropdown-item";
|
||||
import "./ha-picker-field";
|
||||
import type { HaPickerField } from "./ha-picker-field";
|
||||
import "./ha-svg-icon";
|
||||
|
||||
export interface HaSelectOption {
|
||||
value: string;
|
||||
label?: string;
|
||||
secondary?: string;
|
||||
iconPath?: string;
|
||||
disabled?: boolean;
|
||||
}
|
||||
import { SelectBase } from "@material/mwc-select/mwc-select-base";
|
||||
import { styles } from "@material/mwc-select/mwc-select.css";
|
||||
import { mdiClose } from "@mdi/js";
|
||||
import { css, html, nothing } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { classMap } from "lit/directives/class-map";
|
||||
import { debounce } from "../common/util/debounce";
|
||||
import { nextRender } from "../common/util/render-status";
|
||||
import "./ha-icon-button";
|
||||
import "./ha-menu";
|
||||
|
||||
@customElement("ha-select")
|
||||
export class HaSelect extends LitElement {
|
||||
@property({ type: Boolean }) public clearable = false;
|
||||
export class HaSelect extends SelectBase {
|
||||
// @ts-ignore
|
||||
@property({ type: Boolean }) public icon = false;
|
||||
|
||||
@property({ attribute: false }) public options?: HaSelectOption[] | string[];
|
||||
@property({ type: Boolean, reflect: true }) public clearable = false;
|
||||
|
||||
@property() public label?: string;
|
||||
@property({ attribute: "inline-arrow", type: Boolean })
|
||||
public inlineArrow = false;
|
||||
|
||||
@property() public helper?: string;
|
||||
|
||||
@property() public value?: string;
|
||||
|
||||
@property({ type: Boolean }) public required = false;
|
||||
|
||||
@property({ type: Boolean }) public disabled = false;
|
||||
|
||||
@state() private _opened = false;
|
||||
|
||||
@query("ha-picker-field") private _triggerField!: HaPickerField;
|
||||
|
||||
private _getValueLabel = memoizeOne(
|
||||
(
|
||||
options: HaSelectOption[] | string[] | undefined,
|
||||
value: string | undefined
|
||||
) => {
|
||||
if (!options || !value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
for (const option of options) {
|
||||
if (
|
||||
(typeof option === "string" && option === value) ||
|
||||
(typeof option !== "string" && option.value === value)
|
||||
) {
|
||||
return typeof option === "string"
|
||||
? option
|
||||
: option.label || option.value;
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
);
|
||||
@property() public options;
|
||||
|
||||
protected override render() {
|
||||
if (this.disabled) {
|
||||
return this._renderField();
|
||||
}
|
||||
|
||||
return html`
|
||||
<ha-dropdown
|
||||
placement="bottom"
|
||||
@wa-select=${this._handleSelect}
|
||||
@wa-show=${this._handleShow}
|
||||
@wa-hide=${this._handleHide}
|
||||
>
|
||||
${this._renderField()}
|
||||
${this.options
|
||||
? this.options.map(
|
||||
(option) => html`
|
||||
<ha-dropdown-item
|
||||
.value=${typeof option === "string" ? option : option.value}
|
||||
.disabled=${typeof option === "string"
|
||||
? false
|
||||
: (option.disabled ?? false)}
|
||||
class=${this.value ===
|
||||
(typeof option === "string" ? option : option.value)
|
||||
? "selected"
|
||||
: ""}
|
||||
>
|
||||
${option.iconPath
|
||||
? html`<ha-svg-icon
|
||||
slot="icon"
|
||||
.path=${option.iconPath}
|
||||
></ha-svg-icon>`
|
||||
: nothing}
|
||||
<div class="content">
|
||||
${typeof option === "string"
|
||||
? option
|
||||
: option.label || option.value}
|
||||
${option.secondary
|
||||
? html`<div class="secondary">${option.secondary}</div>`
|
||||
: nothing}
|
||||
</div>
|
||||
</ha-dropdown-item>
|
||||
`
|
||||
)
|
||||
: html`<slot></slot>`}
|
||||
</ha-dropdown>
|
||||
${super.render()}
|
||||
${this.clearable && !this.required && !this.disabled && this.value
|
||||
? html`<ha-icon-button
|
||||
label="clear"
|
||||
@click=${this._clearValue}
|
||||
.path=${mdiClose}
|
||||
></ha-icon-button>`
|
||||
: nothing}
|
||||
`;
|
||||
}
|
||||
|
||||
private _renderField() {
|
||||
const valueLabel = this._getValueLabel(this.options, this.value);
|
||||
|
||||
return html`
|
||||
<ha-picker-field
|
||||
slot="trigger"
|
||||
type="button"
|
||||
class=${this._opened ? "opened" : ""}
|
||||
compact
|
||||
aria-label=${ifDefined(this.label)}
|
||||
@clear=${this._clearValue}
|
||||
.label=${this.label}
|
||||
.helper=${this.helper}
|
||||
.value=${valueLabel}
|
||||
.required=${this.required}
|
||||
.disabled=${this.disabled}
|
||||
.hideClearIcon=${!this.clearable ||
|
||||
this.required ||
|
||||
this.disabled ||
|
||||
!this.value}
|
||||
>
|
||||
</ha-picker-field>
|
||||
`;
|
||||
protected override renderMenu() {
|
||||
const classes = this.getMenuClasses();
|
||||
return html`<ha-menu
|
||||
innerRole="listbox"
|
||||
wrapFocus
|
||||
class=${classMap(classes)}
|
||||
activatable
|
||||
.fullwidth=${this.fixedMenuPosition ? false : !this.naturalMenuWidth}
|
||||
.open=${this.menuOpen}
|
||||
.anchor=${this.anchorElement}
|
||||
.fixed=${this.fixedMenuPosition}
|
||||
@selected=${this.onSelected}
|
||||
@opened=${this.onOpened}
|
||||
@closed=${this.onClosed}
|
||||
@items-updated=${this.onItemsUpdated}
|
||||
@keydown=${this.handleTypeahead}
|
||||
>
|
||||
${this.renderMenuContent()}
|
||||
</ha-menu>`;
|
||||
}
|
||||
|
||||
private _handleSelect(ev: CustomEvent<{ item: { value: string } }>) {
|
||||
ev.stopPropagation();
|
||||
const value = ev.detail.item.value;
|
||||
if (value === this.value) {
|
||||
return;
|
||||
protected override renderLeadingIcon() {
|
||||
if (!this.icon) {
|
||||
return nothing;
|
||||
}
|
||||
fireEvent(this, "selected", { value });
|
||||
|
||||
return html`<span class="mdc-select__icon"
|
||||
><slot name="icon"></slot
|
||||
></span>`;
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
window.addEventListener("translations-updated", this._translationsUpdated);
|
||||
}
|
||||
|
||||
protected async firstUpdated() {
|
||||
super.firstUpdated();
|
||||
|
||||
if (this.inlineArrow) {
|
||||
this.shadowRoot
|
||||
?.querySelector(".mdc-select__selected-text-container")
|
||||
?.classList.add("inline-arrow");
|
||||
}
|
||||
}
|
||||
|
||||
protected updated(changedProperties) {
|
||||
super.updated(changedProperties);
|
||||
|
||||
if (changedProperties.has("inlineArrow")) {
|
||||
const textContainerElement = this.shadowRoot?.querySelector(
|
||||
".mdc-select__selected-text-container"
|
||||
);
|
||||
if (this.inlineArrow) {
|
||||
textContainerElement?.classList.add("inline-arrow");
|
||||
} else {
|
||||
textContainerElement?.classList.remove("inline-arrow");
|
||||
}
|
||||
}
|
||||
if (changedProperties.get("options")) {
|
||||
this.layoutOptions();
|
||||
this.selectByValue(this.value);
|
||||
}
|
||||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
super.disconnectedCallback();
|
||||
window.removeEventListener(
|
||||
"translations-updated",
|
||||
this._translationsUpdated
|
||||
);
|
||||
}
|
||||
|
||||
private _clearValue(): void {
|
||||
if (this.disabled || !this.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
fireEvent(this, "selected", { value: undefined });
|
||||
this.valueSetDirectly = true;
|
||||
this.select(-1);
|
||||
this.mdcFoundation.handleChange();
|
||||
}
|
||||
|
||||
private _handleShow() {
|
||||
this.style.setProperty(
|
||||
"--select-menu-width",
|
||||
`${this._triggerField.offsetWidth}px`
|
||||
);
|
||||
this._opened = true;
|
||||
}
|
||||
private _translationsUpdated = debounce(async () => {
|
||||
await nextRender();
|
||||
this.layoutOptions();
|
||||
}, 500);
|
||||
|
||||
private _handleHide() {
|
||||
this._opened = false;
|
||||
}
|
||||
|
||||
static styles = css`
|
||||
:host {
|
||||
position: relative;
|
||||
}
|
||||
ha-picker-field.opened {
|
||||
--mdc-text-field-idle-line-color: var(--primary-color);
|
||||
}
|
||||
ha-dropdown-item.selected:hover {
|
||||
background-color: var(--ha-color-fill-primary-quiet-hover);
|
||||
}
|
||||
|
||||
ha-dropdown-item .content {
|
||||
display: flex;
|
||||
gap: var(--ha-space-1);
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
ha-dropdown-item .secondary {
|
||||
font-size: var(--ha-font-size-s);
|
||||
color: var(--ha-color-text-secondary);
|
||||
}
|
||||
|
||||
ha-dropdown::part(menu) {
|
||||
min-width: var(--select-menu-width);
|
||||
}
|
||||
|
||||
:host ::slotted(ha-dropdown-item.selected),
|
||||
ha-dropdown-item.selected {
|
||||
font-weight: var(--ha-font-weight-medium);
|
||||
color: var(--primary-color);
|
||||
background-color: var(--ha-color-fill-primary-quiet-resting);
|
||||
--icon-primary-color: var(--primary-color);
|
||||
}
|
||||
`;
|
||||
static override styles = [
|
||||
styles,
|
||||
css`
|
||||
:host([clearable]) {
|
||||
position: relative;
|
||||
}
|
||||
.mdc-select:not(.mdc-select--disabled) .mdc-select__icon {
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
.mdc-select__anchor {
|
||||
width: var(--ha-select-min-width, 200px);
|
||||
}
|
||||
.mdc-select--filled .mdc-select__anchor {
|
||||
height: var(--ha-select-height, 56px);
|
||||
}
|
||||
.mdc-select--filled .mdc-floating-label {
|
||||
inset-inline-start: var(--ha-space-4);
|
||||
inset-inline-end: initial;
|
||||
direction: var(--direction);
|
||||
}
|
||||
.mdc-select--filled.mdc-select--with-leading-icon .mdc-floating-label {
|
||||
inset-inline-start: 48px;
|
||||
inset-inline-end: initial;
|
||||
direction: var(--direction);
|
||||
}
|
||||
.mdc-select .mdc-select__anchor {
|
||||
padding-inline-start: var(--ha-space-4);
|
||||
padding-inline-end: 0px;
|
||||
direction: var(--direction);
|
||||
}
|
||||
.mdc-select__anchor .mdc-floating-label--float-above {
|
||||
transform-origin: var(--float-start);
|
||||
}
|
||||
.mdc-select__selected-text-container {
|
||||
padding-inline-end: var(--select-selected-text-padding-end, 0px);
|
||||
}
|
||||
:host([clearable]) .mdc-select__selected-text-container {
|
||||
padding-inline-end: var(
|
||||
--select-selected-text-padding-end,
|
||||
var(--ha-space-4)
|
||||
);
|
||||
}
|
||||
ha-icon-button {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
right: 28px;
|
||||
--mdc-icon-button-size: 36px;
|
||||
--mdc-icon-size: 20px;
|
||||
color: var(--secondary-text-color);
|
||||
inset-inline-start: initial;
|
||||
inset-inline-end: 28px;
|
||||
direction: var(--direction);
|
||||
}
|
||||
.inline-arrow {
|
||||
flex-grow: 0;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ha-select": HaSelect;
|
||||
}
|
||||
|
||||
interface HASSDomEvents {
|
||||
selected: { value: string | undefined };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,16 +5,17 @@ import { repeat } from "lit/directives/repeat";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { ensureArray } from "../../common/array/ensure-array";
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
import { stopPropagation } from "../../common/dom/stop_propagation";
|
||||
import { caseInsensitiveStringCompare } from "../../common/string/compare";
|
||||
import type { SelectOption, SelectSelector } from "../../data/selector";
|
||||
import type { HomeAssistant } from "../../types";
|
||||
import "../chips/ha-chip-set";
|
||||
import "../chips/ha-input-chip";
|
||||
import "../ha-checkbox";
|
||||
import "../ha-dropdown-item";
|
||||
import "../ha-formfield";
|
||||
import "../ha-generic-picker";
|
||||
import "../ha-input-helper-text";
|
||||
import "../ha-list-item";
|
||||
import "../ha-radio";
|
||||
import "../ha-select";
|
||||
import "../ha-select-box";
|
||||
@@ -230,15 +231,24 @@ export class HaSelectSelector extends LitElement {
|
||||
|
||||
return html`
|
||||
<ha-select
|
||||
fixedMenuPosition
|
||||
naturalMenuWidth
|
||||
.label=${this.label ?? ""}
|
||||
.value=${(this.value as string) ?? ""}
|
||||
.value=${this.value ?? ""}
|
||||
.helper=${this.helper ?? ""}
|
||||
.disabled=${this.disabled}
|
||||
.required=${this.required}
|
||||
clearable
|
||||
@closed=${stopPropagation}
|
||||
@selected=${this._valueChanged}
|
||||
.options=${options}
|
||||
>
|
||||
${options.map(
|
||||
(item: SelectOption) => html`
|
||||
<ha-list-item .value=${item.value} .disabled=${!!item.disabled}
|
||||
>${item.label}</ha-list-item
|
||||
>
|
||||
`
|
||||
)}
|
||||
</ha-select>
|
||||
`;
|
||||
}
|
||||
@@ -285,7 +295,7 @@ export class HaSelectSelector extends LitElement {
|
||||
private _valueChanged(ev) {
|
||||
ev.stopPropagation();
|
||||
|
||||
if (ev.detail?.value === undefined && this.value !== undefined) {
|
||||
if (ev.detail?.index === -1 && this.value !== undefined) {
|
||||
fireEvent(this, "value-changed", {
|
||||
value: undefined,
|
||||
});
|
||||
@@ -375,7 +385,7 @@ export class HaSelectSelector extends LitElement {
|
||||
ha-formfield {
|
||||
display: block;
|
||||
}
|
||||
ha-dropdown-item[disabled] {
|
||||
ha-list-item[disabled] {
|
||||
--mdc-theme-text-primary-on-background: var(--disabled-text-color);
|
||||
}
|
||||
ha-chip-set {
|
||||
|
||||
@@ -108,7 +108,6 @@ export class HaSettingsRow extends LitElement {
|
||||
white-space: normal;
|
||||
}
|
||||
.prefix-wrap {
|
||||
flex: 1;
|
||||
display: var(--settings-row-prefix-display);
|
||||
}
|
||||
:host([narrow]) .prefix-wrap {
|
||||
|
||||
@@ -492,22 +492,19 @@ class HaSidebar extends SubscribeMixin(ScrollableFadeMixin(LitElement)) {
|
||||
@mouseleave=${this._itemMouseLeave}
|
||||
>
|
||||
<ha-svg-icon slot="start" .path=${mdiCog}></ha-svg-icon>
|
||||
${this._updatesCount > 0 || this._issuesCount > 0
|
||||
? html`
|
||||
<span class="badge" slot="start">
|
||||
${this._updatesCount + this._issuesCount}
|
||||
</span>
|
||||
`
|
||||
${!this.alwaysExpand &&
|
||||
(this._updatesCount > 0 || this._issuesCount > 0)
|
||||
? html`<span class="badge" slot="start"
|
||||
>${this._updatesCount + this._issuesCount}</span
|
||||
>`
|
||||
: nothing}
|
||||
<span class="item-text" slot="headline"
|
||||
>${this.hass.localize("panel.config")}</span
|
||||
>
|
||||
${this._updatesCount > 0 || this._issuesCount > 0
|
||||
? html`
|
||||
<span class="badge" slot="end"
|
||||
>${this._updatesCount + this._issuesCount}</span
|
||||
>
|
||||
`
|
||||
${this.alwaysExpand && (this._updatesCount > 0 || this._issuesCount > 0)
|
||||
? html`<span class="badge" slot="end"
|
||||
>${this._updatesCount + this._issuesCount}</span
|
||||
>`
|
||||
: nothing}
|
||||
</ha-md-list-item>
|
||||
`;
|
||||
@@ -527,15 +524,13 @@ class HaSidebar extends SubscribeMixin(ScrollableFadeMixin(LitElement)) {
|
||||
type="button"
|
||||
>
|
||||
<ha-svg-icon slot="start" .path=${mdiBell}></ha-svg-icon>
|
||||
${notificationCount > 0
|
||||
? html`
|
||||
<span class="badge" slot="start"> ${notificationCount} </span>
|
||||
`
|
||||
${!this.alwaysExpand && notificationCount > 0
|
||||
? html`<span class="badge" slot="start">${notificationCount}</span>`
|
||||
: nothing}
|
||||
<span class="item-text" slot="headline"
|
||||
>${this.hass.localize("ui.notification_drawer.title")}</span
|
||||
>
|
||||
${notificationCount > 0
|
||||
${this.alwaysExpand && notificationCount > 0
|
||||
? html`<span class="badge" slot="end">${notificationCount}</span>`
|
||||
: nothing}
|
||||
</ha-md-list-item>
|
||||
@@ -744,8 +739,6 @@ class HaSidebar extends SubscribeMixin(ScrollableFadeMixin(LitElement)) {
|
||||
);
|
||||
font-size: var(--ha-font-size-xl);
|
||||
align-items: center;
|
||||
overflow: hidden;
|
||||
width: calc(56px + var(--safe-area-inset-left, 0px));
|
||||
padding-left: calc(
|
||||
var(--ha-space-1) + var(--safe-area-inset-left, 0px)
|
||||
);
|
||||
@@ -754,7 +747,6 @@ class HaSidebar extends SubscribeMixin(ScrollableFadeMixin(LitElement)) {
|
||||
);
|
||||
padding-inline-end: initial;
|
||||
padding-top: var(--safe-area-inset-top, 0px);
|
||||
transition: width var(--ha-animation-duration-normal) ease;
|
||||
}
|
||||
:host([expanded]) .menu {
|
||||
width: calc(256px + var(--safe-area-inset-left, 0px));
|
||||
@@ -769,22 +761,15 @@ class HaSidebar extends SubscribeMixin(ScrollableFadeMixin(LitElement)) {
|
||||
margin-left: 3px;
|
||||
margin-inline-start: 3px;
|
||||
margin-inline-end: initial;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
max-width: 0;
|
||||
opacity: 0;
|
||||
transition:
|
||||
max-width var(--ha-animation-duration-normal) ease,
|
||||
opacity var(--ha-animation-duration-normal) ease;
|
||||
width: 100%;
|
||||
display: none;
|
||||
}
|
||||
:host([narrow]) .title {
|
||||
margin: 0;
|
||||
padding: 0 var(--ha-space-4);
|
||||
}
|
||||
:host([expanded]) .title {
|
||||
max-width: 100%;
|
||||
opacity: 1;
|
||||
transition-delay: 0ms, 80ms;
|
||||
display: initial;
|
||||
}
|
||||
|
||||
.panels-list {
|
||||
@@ -842,7 +827,6 @@ class HaSidebar extends SubscribeMixin(ScrollableFadeMixin(LitElement)) {
|
||||
--md-list-item-leading-space: var(--ha-space-3);
|
||||
--md-list-item-trailing-space: var(--ha-space-3);
|
||||
--md-list-item-leading-icon-size: var(--ha-space-6);
|
||||
transition: width var(--ha-animation-duration-normal) ease;
|
||||
}
|
||||
:host([expanded]) ha-md-list-item {
|
||||
width: 248px;
|
||||
@@ -883,22 +867,11 @@ class HaSidebar extends SubscribeMixin(ScrollableFadeMixin(LitElement)) {
|
||||
}
|
||||
|
||||
ha-md-list-item .item-text {
|
||||
display: block;
|
||||
max-width: 0;
|
||||
opacity: 0;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
display: none;
|
||||
font-size: var(--ha-font-size-m);
|
||||
font-weight: var(--ha-font-weight-medium);
|
||||
transition:
|
||||
max-width var(--ha-animation-duration-normal) ease,
|
||||
opacity var(--ha-animation-duration-normal) ease;
|
||||
}
|
||||
:host([expanded]) ha-md-list-item .item-text {
|
||||
max-width: 100%;
|
||||
opacity: 1;
|
||||
transition-delay: 0ms, 80ms;
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
@@ -916,9 +889,6 @@ class HaSidebar extends SubscribeMixin(ScrollableFadeMixin(LitElement)) {
|
||||
background-color: var(--accent-color);
|
||||
padding: 2px 6px;
|
||||
color: var(--text-accent-color, var(--text-primary-color));
|
||||
transition:
|
||||
opacity var(--ha-animation-duration-normal) ease,
|
||||
transform var(--ha-animation-duration-normal) ease;
|
||||
}
|
||||
|
||||
ha-svg-icon + .badge {
|
||||
@@ -930,12 +900,6 @@ class HaSidebar extends SubscribeMixin(ScrollableFadeMixin(LitElement)) {
|
||||
line-height: var(--ha-line-height-expanded);
|
||||
padding: 0 var(--ha-space-1);
|
||||
}
|
||||
:host([expanded]) .badge[slot="start"],
|
||||
:host(:not([expanded])) .badge[slot="end"] {
|
||||
opacity: 0;
|
||||
transform: scale(0.8);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
ha-md-list-item.user {
|
||||
--md-list-item-leading-icon-size: var(--ha-space-10);
|
||||
@@ -974,15 +938,6 @@ class HaSidebar extends SubscribeMixin(ScrollableFadeMixin(LitElement)) {
|
||||
-webkit-transform: scaleX(var(--scale-direction));
|
||||
transform: scaleX(var(--scale-direction));
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.menu,
|
||||
ha-md-list-item,
|
||||
ha-md-list-item .item-text,
|
||||
.title {
|
||||
transition: none;
|
||||
}
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -2,14 +2,16 @@ import type { PropertyValues } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
import { computeDomain } from "../common/entity/compute_domain";
|
||||
import { stopPropagation } from "../common/dom/stop_propagation";
|
||||
import { computeStateName } from "../common/entity/compute_state_name";
|
||||
import { debounce } from "../common/util/debounce";
|
||||
import type { STTEngine } from "../data/stt";
|
||||
import { listSTTEngines } from "../data/stt";
|
||||
import type { HomeAssistant } from "../types";
|
||||
import "./ha-list-item";
|
||||
import "./ha-select";
|
||||
import type { HaSelectOption } from "./ha-select";
|
||||
import type { HaSelect } from "./ha-select";
|
||||
import { computeDomain } from "../common/entity/compute_domain";
|
||||
|
||||
const NONE = "__NONE_OPTION__";
|
||||
|
||||
@@ -59,30 +61,6 @@ export class HaSTTPicker extends LitElement {
|
||||
value = NONE;
|
||||
}
|
||||
|
||||
const options: HaSelectOption[] = this._engines
|
||||
.filter((engine) => !engine.deprecated || engine.engine_id !== value)
|
||||
.map((engine) => {
|
||||
let label: string;
|
||||
if (engine.engine_id.includes(".")) {
|
||||
const stateObj = this.hass.states[engine.engine_id];
|
||||
label = stateObj ? computeStateName(stateObj) : engine.engine_id;
|
||||
} else {
|
||||
label = engine.name || engine.engine_id;
|
||||
}
|
||||
return {
|
||||
value: engine.engine_id,
|
||||
label,
|
||||
disabled: engine.supported_languages?.length === 0,
|
||||
};
|
||||
});
|
||||
|
||||
if (this.required || value === NONE) {
|
||||
options.unshift({
|
||||
value: NONE,
|
||||
label: this.hass.localize("ui.components.stt-picker.none") || "None",
|
||||
});
|
||||
}
|
||||
|
||||
return html`
|
||||
<ha-select
|
||||
.label=${this.label ||
|
||||
@@ -91,8 +69,33 @@ export class HaSTTPicker extends LitElement {
|
||||
.required=${this.required}
|
||||
.disabled=${this.disabled}
|
||||
@selected=${this._changed}
|
||||
.options=${options}
|
||||
@closed=${stopPropagation}
|
||||
fixedMenuPosition
|
||||
naturalMenuWidth
|
||||
>
|
||||
${!this.required
|
||||
? html`<ha-list-item .value=${NONE}>
|
||||
${this.hass!.localize("ui.components.stt-picker.none")}
|
||||
</ha-list-item>`
|
||||
: nothing}
|
||||
${this._engines.map((engine) => {
|
||||
if (engine.deprecated && engine.engine_id !== value) {
|
||||
return nothing;
|
||||
}
|
||||
let label: string;
|
||||
if (engine.engine_id.includes(".")) {
|
||||
const stateObj = this.hass!.states[engine.engine_id];
|
||||
label = stateObj ? computeStateName(stateObj) : engine.engine_id;
|
||||
} else {
|
||||
label = engine.name || engine.engine_id;
|
||||
}
|
||||
return html`<ha-list-item
|
||||
.value=${engine.engine_id}
|
||||
.disabled=${engine.supported_languages?.length === 0}
|
||||
>
|
||||
${label}
|
||||
</ha-list-item>`;
|
||||
})}
|
||||
</ha-select>
|
||||
`;
|
||||
}
|
||||
@@ -141,17 +144,17 @@ export class HaSTTPicker extends LitElement {
|
||||
}
|
||||
`;
|
||||
|
||||
private _changed(ev: CustomEvent<{ value: string }>): void {
|
||||
const value = ev.detail.value;
|
||||
private _changed(ev): void {
|
||||
const target = ev.target as HaSelect;
|
||||
if (
|
||||
!this.hass ||
|
||||
value === "" ||
|
||||
value === this.value ||
|
||||
(this.value === undefined && value === NONE)
|
||||
target.value === "" ||
|
||||
target.value === this.value ||
|
||||
(this.value === undefined && target.value === NONE)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
this.value = value === NONE ? undefined : value;
|
||||
this.value = target.value === NONE ? undefined : target.value;
|
||||
fireEvent(this, "value-changed", { value: this.value });
|
||||
fireEvent(this, "supported-languages-changed", {
|
||||
value: this._engines!.find((engine) => engine.engine_id === this.value)
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import type { TemplateResult } from "lit";
|
||||
import { css, html, LitElement } from "lit";
|
||||
import { css, html, nothing, LitElement } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
import { stopPropagation } from "../common/dom/stop_propagation";
|
||||
import type { HomeAssistant } from "../types";
|
||||
import "./ha-select";
|
||||
import type { HaSelectOption } from "./ha-select";
|
||||
import "./ha-list-item";
|
||||
|
||||
const DEFAULT_THEME = "default";
|
||||
|
||||
@@ -24,26 +25,6 @@ export class HaThemePicker extends LitElement {
|
||||
@property({ type: Boolean }) public required = false;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
const options: HaSelectOption[] = Object.keys(
|
||||
this.hass?.themes.themes || {}
|
||||
).map((theme) => ({
|
||||
value: theme,
|
||||
}));
|
||||
|
||||
if (this.includeDefault) {
|
||||
options.unshift({
|
||||
value: DEFAULT_THEME,
|
||||
label: "Home Assistant",
|
||||
});
|
||||
}
|
||||
|
||||
if (!this.required) {
|
||||
options.unshift({
|
||||
value: "remove",
|
||||
label: this.hass!.localize("ui.components.theme-picker.no_theme"),
|
||||
});
|
||||
}
|
||||
|
||||
return html`
|
||||
<ha-select
|
||||
.label=${this.label ||
|
||||
@@ -52,8 +33,31 @@ export class HaThemePicker extends LitElement {
|
||||
.required=${this.required}
|
||||
.disabled=${this.disabled}
|
||||
@selected=${this._changed}
|
||||
.options=${options}
|
||||
></ha-select>
|
||||
@closed=${stopPropagation}
|
||||
fixedMenuPosition
|
||||
naturalMenuWidth
|
||||
>
|
||||
${!this.required
|
||||
? html`
|
||||
<ha-list-item value="remove">
|
||||
${this.hass!.localize("ui.components.theme-picker.no_theme")}
|
||||
</ha-list-item>
|
||||
`
|
||||
: nothing}
|
||||
${this.includeDefault
|
||||
? html`
|
||||
<ha-list-item .value=${DEFAULT_THEME}>
|
||||
Home Assistant
|
||||
</ha-list-item>
|
||||
`
|
||||
: nothing}
|
||||
${Object.keys(this.hass!.themes.themes)
|
||||
.sort()
|
||||
.map(
|
||||
(theme) =>
|
||||
html`<ha-list-item .value=${theme}>${theme}</ha-list-item>`
|
||||
)}
|
||||
</ha-select>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -63,11 +67,11 @@ export class HaThemePicker extends LitElement {
|
||||
}
|
||||
`;
|
||||
|
||||
private _changed(ev: CustomEvent<{ value: string }>): void {
|
||||
if (!this.hass || ev.detail.value === "") {
|
||||
private _changed(ev): void {
|
||||
if (!this.hass || ev.target.value === "") {
|
||||
return;
|
||||
}
|
||||
this.value = ev.detail.value === "remove" ? undefined : ev.detail.value;
|
||||
this.value = ev.target.value === "remove" ? undefined : ev.target.value;
|
||||
fireEvent(this, "value-changed", { value: this.value });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,19 +36,10 @@ export class HaTopAppBarFixed extends TopAppBarFixedBase {
|
||||
);
|
||||
padding-top: var(--safe-area-inset-top);
|
||||
padding-right: var(--safe-area-inset-right);
|
||||
transition:
|
||||
width var(--ha-animation-duration-normal) ease,
|
||||
padding-left var(--ha-animation-duration-normal) ease,
|
||||
padding-right var(--ha-animation-duration-normal) ease;
|
||||
}
|
||||
:host([narrow]) .mdc-top-app-bar {
|
||||
padding-left: var(--safe-area-inset-left);
|
||||
}
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.mdc-top-app-bar {
|
||||
transition: none;
|
||||
}
|
||||
}
|
||||
.mdc-top-app-bar__title {
|
||||
font-size: var(--ha-font-size-xl);
|
||||
padding-inline-start: var(--ha-space-6);
|
||||
|
||||
@@ -2,14 +2,16 @@ import type { PropertyValues } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
import { computeDomain } from "../common/entity/compute_domain";
|
||||
import { stopPropagation } from "../common/dom/stop_propagation";
|
||||
import { computeStateName } from "../common/entity/compute_state_name";
|
||||
import { debounce } from "../common/util/debounce";
|
||||
import type { TTSEngine } from "../data/tts";
|
||||
import { listTTSEngines } from "../data/tts";
|
||||
import type { HomeAssistant } from "../types";
|
||||
import "./ha-list-item";
|
||||
import "./ha-select";
|
||||
import type { HaSelectOption } from "./ha-select";
|
||||
import type { HaSelect } from "./ha-select";
|
||||
import { computeDomain } from "../common/entity/compute_domain";
|
||||
|
||||
const NONE = "__NONE_OPTION__";
|
||||
|
||||
@@ -59,30 +61,6 @@ export class HaTTSPicker extends LitElement {
|
||||
value = NONE;
|
||||
}
|
||||
|
||||
const options: HaSelectOption[] = this._engines
|
||||
.filter((engine) => !engine.deprecated || engine.engine_id === value)
|
||||
.map((engine) => {
|
||||
let label: string;
|
||||
if (engine.engine_id.includes(".")) {
|
||||
const stateObj = this.hass.states[engine.engine_id];
|
||||
label = stateObj ? computeStateName(stateObj) : engine.engine_id;
|
||||
} else {
|
||||
label = engine.name || engine.engine_id;
|
||||
}
|
||||
return {
|
||||
value: engine.engine_id,
|
||||
label,
|
||||
disabled: engine.supported_languages?.length === 0,
|
||||
};
|
||||
});
|
||||
|
||||
if (!this.required || value === NONE) {
|
||||
options.unshift({
|
||||
value: NONE,
|
||||
label: this.hass.localize("ui.components.tts-picker.none"),
|
||||
});
|
||||
}
|
||||
|
||||
return html`
|
||||
<ha-select
|
||||
.label=${this.label ||
|
||||
@@ -91,8 +69,33 @@ export class HaTTSPicker extends LitElement {
|
||||
.required=${this.required}
|
||||
.disabled=${this.disabled}
|
||||
@selected=${this._changed}
|
||||
.options=${options}
|
||||
@closed=${stopPropagation}
|
||||
fixedMenuPosition
|
||||
naturalMenuWidth
|
||||
>
|
||||
${!this.required
|
||||
? html`<ha-list-item .value=${NONE}>
|
||||
${this.hass!.localize("ui.components.tts-picker.none")}
|
||||
</ha-list-item>`
|
||||
: nothing}
|
||||
${this._engines.map((engine) => {
|
||||
if (engine.deprecated && engine.engine_id !== value) {
|
||||
return nothing;
|
||||
}
|
||||
let label: string;
|
||||
if (engine.engine_id.includes(".")) {
|
||||
const stateObj = this.hass!.states[engine.engine_id];
|
||||
label = stateObj ? computeStateName(stateObj) : engine.engine_id;
|
||||
} else {
|
||||
label = engine.name || engine.engine_id;
|
||||
}
|
||||
return html`<ha-list-item
|
||||
.value=${engine.engine_id}
|
||||
.disabled=${engine.supported_languages?.length === 0}
|
||||
>
|
||||
${label}
|
||||
</ha-list-item>`;
|
||||
})}
|
||||
</ha-select>
|
||||
`;
|
||||
}
|
||||
@@ -141,17 +144,17 @@ export class HaTTSPicker extends LitElement {
|
||||
}
|
||||
`;
|
||||
|
||||
private _changed(ev: CustomEvent<{ value: string }>): void {
|
||||
const value = ev.detail.value;
|
||||
private _changed(ev): void {
|
||||
const target = ev.target as HaSelect;
|
||||
if (
|
||||
!this.hass ||
|
||||
value === "" ||
|
||||
value === this.value ||
|
||||
(this.value === undefined && value === NONE)
|
||||
target.value === "" ||
|
||||
target.value === this.value ||
|
||||
(this.value === undefined && target.value === NONE)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
this.value = value === NONE ? undefined : value;
|
||||
this.value = target.value === NONE ? undefined : target.value;
|
||||
fireEvent(this, "value-changed", { value: this.value });
|
||||
fireEvent(this, "supported-languages-changed", {
|
||||
value: this._engines!.find((engine) => engine.engine_id === this.value)
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
import type { PropertyValues } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
import { stopPropagation } from "../common/dom/stop_propagation";
|
||||
import { debounce } from "../common/util/debounce";
|
||||
import type { TTSVoice } from "../data/tts";
|
||||
import { listTTSVoices } from "../data/tts";
|
||||
import type { HomeAssistant } from "../types";
|
||||
import "./ha-list-item";
|
||||
import "./ha-select";
|
||||
import type { HaSelectOption } from "./ha-select";
|
||||
import type { HaSelect } from "./ha-select";
|
||||
|
||||
const NONE = "__NONE_OPTION__";
|
||||
|
||||
@@ -29,25 +31,14 @@ export class HaTTSVoicePicker extends LitElement {
|
||||
|
||||
@state() _voices?: TTSVoice[] | null;
|
||||
|
||||
@query("ha-select") private _select?: HaSelect;
|
||||
|
||||
protected render() {
|
||||
if (!this._voices) {
|
||||
return nothing;
|
||||
}
|
||||
const value =
|
||||
this.value ?? (this.required ? this._voices[0]?.voice_id : NONE);
|
||||
|
||||
const options: HaSelectOption[] = (this._voices || []).map((voice) => ({
|
||||
value: voice.voice_id,
|
||||
label: voice.name,
|
||||
}));
|
||||
|
||||
if (!this.required || !this.value) {
|
||||
options.unshift({
|
||||
value: NONE,
|
||||
label: this.hass!.localize("ui.components.tts-voice-picker.none"),
|
||||
});
|
||||
}
|
||||
|
||||
return html`
|
||||
<ha-select
|
||||
.label=${this.label ||
|
||||
@@ -56,8 +47,21 @@ export class HaTTSVoicePicker extends LitElement {
|
||||
.required=${this.required}
|
||||
.disabled=${this.disabled}
|
||||
@selected=${this._changed}
|
||||
.options=${options}
|
||||
@closed=${stopPropagation}
|
||||
fixedMenuPosition
|
||||
naturalMenuWidth
|
||||
>
|
||||
${!this.required
|
||||
? html`<ha-list-item .value=${NONE}>
|
||||
${this.hass!.localize("ui.components.tts-voice-picker.none")}
|
||||
</ha-list-item>`
|
||||
: nothing}
|
||||
${this._voices.map(
|
||||
(voice) =>
|
||||
html`<ha-list-item .value=${voice.voice_id}>
|
||||
${voice.name}
|
||||
</ha-list-item>`
|
||||
)}
|
||||
</ha-select>
|
||||
`;
|
||||
}
|
||||
@@ -98,25 +102,34 @@ export class HaTTSVoicePicker extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
protected updated(changedProperties: PropertyValues<this>) {
|
||||
super.updated(changedProperties);
|
||||
if (
|
||||
changedProperties.has("_voices") &&
|
||||
this._select?.value !== this.value
|
||||
) {
|
||||
this._select?.layoutOptions();
|
||||
fireEvent(this, "value-changed", { value: this._select?.value });
|
||||
}
|
||||
}
|
||||
|
||||
static styles = css`
|
||||
ha-select {
|
||||
width: 100%;
|
||||
text-align: start;
|
||||
display: block;
|
||||
}
|
||||
`;
|
||||
|
||||
private _changed(ev: CustomEvent<{ value: string }>): void {
|
||||
const value = ev.detail.value;
|
||||
private _changed(ev): void {
|
||||
const target = ev.target as HaSelect;
|
||||
if (
|
||||
!this.hass ||
|
||||
value === "" ||
|
||||
value === this.value ||
|
||||
(this.value === undefined && value === NONE)
|
||||
target.value === "" ||
|
||||
target.value === this.value ||
|
||||
(this.value === undefined && target.value === NONE)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
this.value = value === NONE ? undefined : value;
|
||||
this.value = target.value === NONE ? undefined : target.value;
|
||||
fireEvent(this, "value-changed", { value: this.value });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -288,19 +288,10 @@ export class TopAppBarBaseBase extends BaseElement {
|
||||
);
|
||||
padding-top: var(--safe-area-inset-top);
|
||||
padding-right: var(--safe-area-inset-right);
|
||||
transition:
|
||||
width var(--ha-animation-duration-normal) ease,
|
||||
padding-left var(--ha-animation-duration-normal) ease,
|
||||
padding-right var(--ha-animation-duration-normal) ease;
|
||||
}
|
||||
:host([narrow]) .mdc-top-app-bar {
|
||||
padding-left: var(--safe-area-inset-left);
|
||||
}
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.mdc-top-app-bar {
|
||||
transition: none;
|
||||
}
|
||||
}
|
||||
.mdc-top-app-bar--pane.mdc-top-app-bar--fixed-scrolled {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import "../../../components/ha-cover-controls";
|
||||
import "../../../components/ha-cover-tilt-controls";
|
||||
import "../../../components/ha-date-input";
|
||||
import "../../../components/ha-humidifier-state";
|
||||
import "../../../components/ha-list-item";
|
||||
import "../../../components/ha-select";
|
||||
import "../../../components/ha-slider";
|
||||
import "../../../components/ha-time-input";
|
||||
@@ -295,11 +296,17 @@ class EntityPreviewRow extends LitElement {
|
||||
.label=${computeStateName(stateObj)}
|
||||
.value=${stateObj.state}
|
||||
.disabled=${isUnavailableState(stateObj.state)}
|
||||
.options=${stateObj.attributes.options?.map((option) => ({
|
||||
value: option,
|
||||
label: this.hass!.formatEntityState(stateObj, option),
|
||||
})) || []}
|
||||
naturalMenuWidth
|
||||
>
|
||||
${stateObj.attributes.options
|
||||
? stateObj.attributes.options.map(
|
||||
(option) => html`
|
||||
<ha-list-item .value=${option}>
|
||||
${this.hass!.formatEntityState(stateObj, option)}
|
||||
</ha-list-item>
|
||||
`
|
||||
)
|
||||
: ""}
|
||||
</ha-select>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -1,18 +1,23 @@
|
||||
import { mdiPlay, mdiStop } from "@mdi/js";
|
||||
import { mdiClose, mdiPlay, mdiStop } from "@mdi/js";
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import type { CSSResultGroup } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import { stopPropagation } from "../../../../common/dom/stop_propagation";
|
||||
import { supportsFeature } from "../../../../common/entity/supports-feature";
|
||||
import "../../../../components/ha-button";
|
||||
import "../../../../components/ha-control-button";
|
||||
import "../../../../components/ha-dialog-footer";
|
||||
import "../../../../components/ha-dialog-header";
|
||||
import "../../../../components/ha-icon-button";
|
||||
import "../../../../components/ha-list-item";
|
||||
import type { HaMdDialog } from "../../../../components/ha-md-dialog";
|
||||
import {
|
||||
getMobileCloseToBottomAnimation,
|
||||
getMobileOpenFromBottomAnimation,
|
||||
} from "../../../../components/ha-md-dialog";
|
||||
import "../../../../components/ha-select";
|
||||
import "../../../../components/ha-textfield";
|
||||
import "../../../../components/ha-wa-dialog";
|
||||
import { SirenEntityFeature } from "../../../../data/siren";
|
||||
import { haStyle } from "../../../../resources/styles";
|
||||
import type { HomeAssistant } from "../../../../types";
|
||||
@@ -23,25 +28,23 @@ class MoreInfoSirenAdvancedControls extends LitElement {
|
||||
|
||||
@state() _stateObj?: HassEntity;
|
||||
|
||||
@state() private _open = false;
|
||||
|
||||
@state() _tone?: string;
|
||||
|
||||
@state() _volume?: number;
|
||||
|
||||
@state() _duration?: number;
|
||||
|
||||
@query("ha-md-dialog") private _dialog?: HaMdDialog;
|
||||
|
||||
public showDialog({ stateObj }: { stateObj: HassEntity }) {
|
||||
this._stateObj = stateObj;
|
||||
this._open = true;
|
||||
}
|
||||
|
||||
public closeDialog(): void {
|
||||
this._open = false;
|
||||
this._dialog?.close();
|
||||
}
|
||||
|
||||
private _dialogClosed(): void {
|
||||
this._open = false;
|
||||
this._stateObj = undefined;
|
||||
fireEvent(this, "dialog-closed", { dialog: this.localName });
|
||||
}
|
||||
@@ -62,33 +65,50 @@ class MoreInfoSirenAdvancedControls extends LitElement {
|
||||
SirenEntityFeature.DURATION
|
||||
);
|
||||
return html`
|
||||
<ha-wa-dialog
|
||||
.open=${this._open}
|
||||
.hass=${this.hass}
|
||||
header-title=${this.hass.localize(
|
||||
"ui.components.siren.advanced_controls"
|
||||
)}
|
||||
<ha-md-dialog
|
||||
open
|
||||
@closed=${this._dialogClosed}
|
||||
aria-labelledby="dialog-light-color-favorite-title"
|
||||
.getOpenAnimation=${getMobileOpenFromBottomAnimation}
|
||||
.getCloseAnimation=${getMobileCloseToBottomAnimation}
|
||||
>
|
||||
<div>
|
||||
<ha-dialog-header slot="headline">
|
||||
<ha-icon-button
|
||||
slot="navigationIcon"
|
||||
@click=${this.closeDialog}
|
||||
.label=${this.hass.localize("ui.common.close")}
|
||||
.path=${mdiClose}
|
||||
></ha-icon-button>
|
||||
<span slot="title" id="dialog-light-color-favorite-title"
|
||||
>${this.hass.localize(
|
||||
"ui.components.siren.advanced_controls"
|
||||
)}</span
|
||||
>
|
||||
</ha-dialog-header>
|
||||
<div slot="content">
|
||||
<div class="options">
|
||||
${supportsTones
|
||||
? html`
|
||||
<ha-select
|
||||
.label=${this.hass.localize("ui.components.siren.tone")}
|
||||
@selected=${this._handleToneChange}
|
||||
@closed=${stopPropagation}
|
||||
@change=${this._handleToneChange}
|
||||
.value=${this._tone}
|
||||
.options=${Object.entries(
|
||||
this._stateObj!.attributes.available_tones
|
||||
).map(([toneId, toneName]) => ({
|
||||
value: Array.isArray(
|
||||
this._stateObj!.attributes.available_tones
|
||||
)
|
||||
? toneName
|
||||
: toneId,
|
||||
label: toneName,
|
||||
}))}
|
||||
>
|
||||
${Object.entries(
|
||||
this._stateObj.attributes.available_tones
|
||||
).map(
|
||||
([toneId, toneName]) => html`
|
||||
<ha-list-item
|
||||
.value=${Array.isArray(
|
||||
this._stateObj!.attributes.available_tones
|
||||
)
|
||||
? toneName
|
||||
: toneId}
|
||||
>${toneName}</ha-list-item
|
||||
>
|
||||
`
|
||||
)}
|
||||
</ha-select>
|
||||
`
|
||||
: nothing}
|
||||
@@ -133,21 +153,17 @@ class MoreInfoSirenAdvancedControls extends LitElement {
|
||||
</ha-control-button>
|
||||
</div>
|
||||
</div>
|
||||
<ha-dialog-footer slot="footer">
|
||||
<ha-button
|
||||
slot="secondaryAction"
|
||||
appearance="plain"
|
||||
@click=${this.closeDialog}
|
||||
>
|
||||
<div slot="actions">
|
||||
<ha-button @click=${this.closeDialog}>
|
||||
${this.hass.localize("ui.common.close")}
|
||||
</ha-button>
|
||||
</ha-dialog-footer>
|
||||
</ha-wa-dialog>
|
||||
</div>
|
||||
</ha-md-dialog>
|
||||
`;
|
||||
}
|
||||
|
||||
private _handleToneChange(ev: CustomEvent<{ value: string }>) {
|
||||
this._tone = ev.detail.value;
|
||||
private _handleToneChange(ev) {
|
||||
this._tone = ev.target.value;
|
||||
}
|
||||
|
||||
private _handleVolumeChange(ev) {
|
||||
|
||||
@@ -202,11 +202,12 @@ class MoreInfoMediaPlayer extends LitElement {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
return html`<ha-dropdown @wa-select=${this._handleSourceChange}>
|
||||
return html`<ha-dropdown>
|
||||
<ha-icon-button
|
||||
slot="trigger"
|
||||
.label=${this.hass.localize(`ui.card.media_player.source`)}
|
||||
.path=${mdiLoginVariant}
|
||||
@wa-select=${this._handleSourceChange}
|
||||
>
|
||||
</ha-icon-button>
|
||||
${this.stateObj.attributes.source_list!.map(
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { stopPropagation } from "../../../common/dom/stop_propagation";
|
||||
import { supportsFeature } from "../../../common/entity/supports-feature";
|
||||
import "../../../components/ha-select";
|
||||
import type { RemoteEntity } from "../../../data/remote";
|
||||
import { REMOTE_SUPPORT_ACTIVITY } from "../../../data/remote";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import "../../../components/ha-select";
|
||||
import "../../../components/ha-list-item";
|
||||
|
||||
@customElement("more-info-remote")
|
||||
class MoreInfoRemote extends LitElement {
|
||||
@@ -28,24 +30,30 @@ class MoreInfoRemote extends LitElement {
|
||||
)}
|
||||
.value=${stateObj.attributes.current_activity || ""}
|
||||
@selected=${this._handleActivityChanged}
|
||||
.options=${stateObj.attributes.activity_list?.map((activity) => ({
|
||||
value: activity,
|
||||
label: this.hass!.formatEntityAttributeValue(
|
||||
stateObj,
|
||||
"activity",
|
||||
activity
|
||||
),
|
||||
}))}
|
||||
fixedMenuPosition
|
||||
naturalMenuWidth
|
||||
@closed=${stopPropagation}
|
||||
>
|
||||
${stateObj.attributes.activity_list?.map(
|
||||
(activity) => html`
|
||||
<ha-list-item .value=${activity}>
|
||||
${this.hass.formatEntityAttributeValue(
|
||||
stateObj,
|
||||
"activity",
|
||||
activity
|
||||
)}
|
||||
</ha-list-item>
|
||||
`
|
||||
)}
|
||||
</ha-select>
|
||||
`
|
||||
: nothing}
|
||||
`;
|
||||
}
|
||||
|
||||
private _handleActivityChanged(ev: CustomEvent<{ value: string }>) {
|
||||
private _handleActivityChanged(ev) {
|
||||
const oldVal = this.stateObj!.attributes.current_activity;
|
||||
const newVal = ev.detail.value;
|
||||
const newVal = ev.target.value;
|
||||
|
||||
if (!newVal || oldVal === newVal) {
|
||||
return;
|
||||
|
||||
@@ -11,11 +11,13 @@ import {
|
||||
import { LitElement, css, html, nothing } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { stopPropagation } from "../../../common/dom/stop_propagation";
|
||||
import { computeStateDomain } from "../../../common/entity/compute_state_domain";
|
||||
import { supportsFeature } from "../../../common/entity/supports-feature";
|
||||
import "../../../components/entity/ha-battery-icon";
|
||||
import "../../../components/ha-icon";
|
||||
import "../../../components/ha-icon-button";
|
||||
import "../../../components/ha-list-item";
|
||||
import "../../../components/ha-select";
|
||||
import { UNAVAILABLE } from "../../../data/entity/entity";
|
||||
import type { EntityRegistryDisplayEntry } from "../../../data/entity/entity_registry";
|
||||
@@ -170,17 +172,21 @@ class MoreInfoVacuum extends LitElement {
|
||||
.disabled=${stateObj.state === UNAVAILABLE}
|
||||
.value=${stateObj.attributes.fan_speed}
|
||||
@selected=${this._handleFanSpeedChanged}
|
||||
.options=${stateObj.attributes.fan_speed_list!.map(
|
||||
(mode) => ({
|
||||
value: mode,
|
||||
label: this.hass!.formatEntityAttributeValue(
|
||||
stateObj,
|
||||
"fan_speed",
|
||||
mode
|
||||
),
|
||||
})
|
||||
)}
|
||||
fixedMenuPosition
|
||||
naturalMenuWidth
|
||||
@closed=${stopPropagation}
|
||||
>
|
||||
${stateObj.attributes.fan_speed_list!.map(
|
||||
(mode) => html`
|
||||
<ha-list-item .value=${mode}>
|
||||
${this.hass.formatEntityAttributeValue(
|
||||
stateObj,
|
||||
"fan_speed",
|
||||
mode
|
||||
)}
|
||||
</ha-list-item>
|
||||
`
|
||||
)}
|
||||
</ha-select>
|
||||
<div
|
||||
style="justify-content: center; align-self: center; padding-top: 1.3em"
|
||||
@@ -285,9 +291,9 @@ class MoreInfoVacuum extends LitElement {
|
||||
});
|
||||
}
|
||||
|
||||
private _handleFanSpeedChanged(ev: CustomEvent<{ value: string }>) {
|
||||
private _handleFanSpeedChanged(ev) {
|
||||
const oldVal = this.stateObj!.attributes.fan_speed;
|
||||
const newVal = ev.detail.value;
|
||||
const newVal = ev.target.value;
|
||||
|
||||
if (!newVal || oldVal === newVal) {
|
||||
return;
|
||||
|
||||
@@ -5,8 +5,8 @@ import "../../components/ha-button";
|
||||
import "../../components/ha-spinner";
|
||||
import { testAssistSatelliteConnection } from "../../data/assist_satellite";
|
||||
import type { HomeAssistant } from "../../types";
|
||||
import { documentationUrl } from "../../util/documentation-url";
|
||||
import { AssistantSetupStyles } from "./styles";
|
||||
import { documentationUrl } from "../../util/documentation-url";
|
||||
|
||||
@customElement("ha-voice-assistant-setup-step-check")
|
||||
export class HaVoiceAssistantSetupStepCheck extends LitElement {
|
||||
@@ -58,7 +58,7 @@ export class HaVoiceAssistantSetupStepCheck extends LitElement {
|
||||
"/voice_control/troubleshooting/#i-dont-get-a-voice-response"
|
||||
)}
|
||||
>
|
||||
${this.hass.localize(
|
||||
>${this.hass.localize(
|
||||
"ui.panel.config.voice_assistants.satellite_wizard.check.help"
|
||||
)}</ha-button
|
||||
>
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
computeDeviceName,
|
||||
computeDeviceNameDisplay,
|
||||
} from "../../common/entity/compute_device_name";
|
||||
import "../../components/ha-list-item";
|
||||
import "../../components/ha-select";
|
||||
import "../../components/ha-tts-voice-picker";
|
||||
import type { AssistPipeline } from "../../data/assist_pipeline";
|
||||
@@ -114,15 +115,19 @@ export class HaVoiceAssistantSetupStepSuccess extends LitElement {
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.config.voice_assistants.assistants.pipeline.detail.form.wake_word_id"
|
||||
)}
|
||||
@closed=${stopPropagation}
|
||||
fixedMenuPosition
|
||||
naturalMenuWidth
|
||||
.value=${this.assistConfiguration.active_wake_words[0]}
|
||||
@selected=${this._wakeWordPicked}
|
||||
.options=${this.assistConfiguration.available_wake_words.map(
|
||||
(wakeword) => ({
|
||||
value: wakeword.id,
|
||||
label: wakeword.wake_word,
|
||||
})
|
||||
>
|
||||
${this.assistConfiguration.available_wake_words.map(
|
||||
(wakeword) =>
|
||||
html`<ha-list-item .value=${wakeword.id}>
|
||||
${wakeword.wake_word}
|
||||
</ha-list-item>`
|
||||
)}
|
||||
></ha-select>
|
||||
</ha-select>
|
||||
<ha-button
|
||||
appearance="plain"
|
||||
size="small"
|
||||
@@ -146,17 +151,16 @@ export class HaVoiceAssistantSetupStepSuccess extends LitElement {
|
||||
)}
|
||||
@closed=${stopPropagation}
|
||||
.value=${pipelineEntity?.state}
|
||||
fixedMenuPosition
|
||||
naturalMenuWidth
|
||||
@selected=${this._pipelinePicked}
|
||||
.options=${pipelineEntity?.attributes.options.map(
|
||||
(pipeline) => ({
|
||||
value: pipeline,
|
||||
label: this.hass.formatEntityState(
|
||||
pipelineEntity,
|
||||
pipeline
|
||||
),
|
||||
})
|
||||
)}
|
||||
>
|
||||
${pipelineEntity?.attributes.options.map(
|
||||
(pipeline) =>
|
||||
html`<ha-list-item .value=${pipeline}>
|
||||
${this.hass.formatEntityState(pipelineEntity, pipeline)}
|
||||
</ha-list-item>`
|
||||
)}
|
||||
</ha-select>
|
||||
<ha-button
|
||||
appearance="plain"
|
||||
@@ -231,19 +235,16 @@ export class HaVoiceAssistantSetupStepSuccess extends LitElement {
|
||||
this._deviceName = ev.target.value;
|
||||
}
|
||||
|
||||
private async _wakeWordPicked(ev: CustomEvent<{ value: string }>) {
|
||||
const option = ev.detail.value;
|
||||
if (this.assistConfiguration) {
|
||||
this.assistConfiguration.active_wake_words = [option];
|
||||
}
|
||||
private async _wakeWordPicked(ev) {
|
||||
const option = ev.target.value;
|
||||
await setWakeWords(this.hass, this.assistEntityId!, [option]);
|
||||
}
|
||||
|
||||
private _pipelinePicked(ev: CustomEvent<{ value: string }>) {
|
||||
private _pipelinePicked(ev) {
|
||||
const stateObj = this.hass!.states[
|
||||
this.assistConfiguration!.pipeline_entity_id
|
||||
] as InputSelectEntity;
|
||||
const option = ev.detail.value;
|
||||
const option = ev.target.value;
|
||||
if (
|
||||
option === stateObj.state ||
|
||||
!stateObj.attributes.options.includes(option)
|
||||
@@ -383,11 +384,6 @@ export class HaVoiceAssistantSetupStepSuccess extends LitElement {
|
||||
.row ha-button {
|
||||
width: 82px;
|
||||
}
|
||||
|
||||
ha-select {
|
||||
display: block;
|
||||
text-align: start;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -29,11 +29,11 @@
|
||||
}
|
||||
}
|
||||
::view-transition-group(launch-screen) {
|
||||
animation-duration: var(--ha-animation-duration-slow, 350ms);
|
||||
animation-duration: var(--ha-animation-base-duration, 350ms);
|
||||
animation-timing-function: ease-out;
|
||||
}
|
||||
::view-transition-old(launch-screen) {
|
||||
animation: fade-out var(--ha-animation-duration-slow, 350ms) ease-out;
|
||||
animation: fade-out var(--ha-animation-base-duration, 350ms) ease-out;
|
||||
}
|
||||
html {
|
||||
background-color: var(--primary-background-color, #fafafa);
|
||||
|
||||
@@ -144,6 +144,7 @@ class HaOnboarding extends litLocalizeLiteMixin(HassElement) {
|
||||
.label=${""}
|
||||
native-name
|
||||
@value-changed=${this._languageChanged}
|
||||
inline-arrow
|
||||
></ha-language-picker>
|
||||
<a
|
||||
href="https://www.home-assistant.io/getting-started/onboarding/"
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
import type { SelectedDetail } from "@material/mwc-list";
|
||||
import { TZDate } from "@date-fns/tz";
|
||||
import type { PropertyValues } from "lit";
|
||||
import { LitElement, css, html, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import type { ByWeekday, Options, WeekdayStr } from "rrule";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import type { Options, WeekdayStr, ByWeekday } from "rrule";
|
||||
import { RRule, Weekday } from "rrule";
|
||||
import { formatDate, formatTime } from "../../common/datetime/calc_date";
|
||||
import { firstWeekdayIndex } from "../../common/datetime/first_weekday";
|
||||
import { stopPropagation } from "../../common/dom/stop_propagation";
|
||||
import type { LocalizeKeys } from "../../common/translations/localize";
|
||||
import "../../components/chips/ha-chip-set";
|
||||
import "../../components/chips/ha-filter-chip";
|
||||
import "../../components/ha-date-input";
|
||||
import "../../components/ha-list-item";
|
||||
import "../../components/ha-select";
|
||||
import type { HaSelect } from "../../components/ha-select";
|
||||
import "../../components/ha-textfield";
|
||||
import type { HomeAssistant } from "../../types";
|
||||
import type {
|
||||
@@ -30,6 +33,7 @@ import {
|
||||
ruleByWeekDay,
|
||||
untilValue,
|
||||
} from "./recurrence";
|
||||
import { formatDate, formatTime } from "../../common/datetime/calc_date";
|
||||
|
||||
@customElement("ha-recurrence-rule-editor")
|
||||
export class RecurrenceRuleEditor extends LitElement {
|
||||
@@ -67,6 +71,8 @@ export class RecurrenceRuleEditor extends LitElement {
|
||||
|
||||
@state() private _untilDay?: Date;
|
||||
|
||||
@query("#monthly") private _monthlyRepeatSelect!: HaSelect;
|
||||
|
||||
private _allWeekdays?: WeekdayStr[];
|
||||
|
||||
private _monthlyRepeatItems: MonthlyRepeatItem[] = [];
|
||||
@@ -85,6 +91,14 @@ export class RecurrenceRuleEditor extends LitElement {
|
||||
? getMonthlyRepeatItems(this.hass, this._interval, this.dtstart)
|
||||
: [];
|
||||
this._computeWeekday();
|
||||
const selectElement = this._monthlyRepeatSelect;
|
||||
if (selectElement) {
|
||||
const oldSelected = selectElement.index;
|
||||
selectElement.select(-1);
|
||||
this.updateComplete.then(() => {
|
||||
selectElement.select(changedProps.has("dtstart") ? 0 : oldSelected);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
@@ -170,16 +184,35 @@ export class RecurrenceRuleEditor extends LitElement {
|
||||
id="freq"
|
||||
label=${this.hass.localize("ui.components.calendar.event.repeat.label")}
|
||||
@selected=${this._onRepeatSelected}
|
||||
@closed=${stopPropagation}
|
||||
fixedMenuPosition
|
||||
naturalMenuWidth
|
||||
.value=${this._freq}
|
||||
.options=${["none", "yearly", "monthly", "weekly", "daily"].map(
|
||||
(freq) => ({
|
||||
value: freq,
|
||||
label: this.hass.localize(
|
||||
`ui.components.calendar.event.repeat.freq.${freq}` as LocalizeKeys
|
||||
),
|
||||
})
|
||||
)}
|
||||
></ha-select>
|
||||
>
|
||||
<ha-list-item value="none">
|
||||
${this.hass.localize("ui.components.calendar.event.repeat.freq.none")}
|
||||
</ha-list-item>
|
||||
<ha-list-item value="yearly">
|
||||
${this.hass.localize(
|
||||
"ui.components.calendar.event.repeat.freq.yearly"
|
||||
)}
|
||||
</ha-list-item>
|
||||
<ha-list-item value="monthly">
|
||||
${this.hass.localize(
|
||||
"ui.components.calendar.event.repeat.freq.monthly"
|
||||
)}
|
||||
</ha-list-item>
|
||||
<ha-list-item value="weekly">
|
||||
${this.hass.localize(
|
||||
"ui.components.calendar.event.repeat.freq.weekly"
|
||||
)}
|
||||
</ha-list-item>
|
||||
<ha-list-item value="daily">
|
||||
${this.hass.localize(
|
||||
"ui.components.calendar.event.repeat.freq.daily"
|
||||
)}
|
||||
</ha-list-item>
|
||||
</ha-select>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -194,8 +227,18 @@ export class RecurrenceRuleEditor extends LitElement {
|
||||
)}
|
||||
@selected=${this._onMonthlyDetailSelected}
|
||||
.value=${this._monthlyRepeat || this._monthlyRepeatItems[0]?.value}
|
||||
.options=${this._monthlyRepeatItems}
|
||||
></ha-select>`
|
||||
@closed=${stopPropagation}
|
||||
fixedMenuPosition
|
||||
naturalMenuWidth
|
||||
>
|
||||
${this._monthlyRepeatItems!.map(
|
||||
(item) => html`
|
||||
<ha-list-item .value=${item.value} .item=${item}>
|
||||
${item.label}
|
||||
</ha-list-item>
|
||||
`
|
||||
)}
|
||||
</ha-select>`
|
||||
: nothing}
|
||||
`;
|
||||
}
|
||||
@@ -256,13 +299,19 @@ export class RecurrenceRuleEditor extends LitElement {
|
||||
)}
|
||||
.value=${this._end}
|
||||
@selected=${this._onEndSelected}
|
||||
.options=${["never", "after", "on"].map((end) => ({
|
||||
value: end,
|
||||
label: this.hass.localize(
|
||||
`ui.components.calendar.event.repeat.end.${end as RepeatEnd}`
|
||||
),
|
||||
}))}
|
||||
@closed=${stopPropagation}
|
||||
fixedMenuPosition
|
||||
naturalMenuWidth
|
||||
>
|
||||
<ha-list-item value="never">
|
||||
${this.hass.localize("ui.components.calendar.event.repeat.end.never")}
|
||||
</ha-list-item>
|
||||
<ha-list-item value="after">
|
||||
${this.hass.localize("ui.components.calendar.event.repeat.end.after")}
|
||||
</ha-list-item>
|
||||
<ha-list-item value="on">
|
||||
${this.hass.localize("ui.components.calendar.event.repeat.end.on")}
|
||||
</ha-list-item>
|
||||
</ha-select>
|
||||
${this._end === "after"
|
||||
? html`
|
||||
@@ -311,8 +360,8 @@ export class RecurrenceRuleEditor extends LitElement {
|
||||
this._interval = (e.target! as any).value;
|
||||
}
|
||||
|
||||
private _onRepeatSelected(e: CustomEvent<{ value: string }>) {
|
||||
this._freq = e.detail.value as RepeatFrequency;
|
||||
private _onRepeatSelected(e: CustomEvent<SelectedDetail<number>>) {
|
||||
this._freq = (e.target as HaSelect).value as RepeatFrequency;
|
||||
|
||||
if (this._freq === "yearly") {
|
||||
this._interval = 1;
|
||||
@@ -321,12 +370,12 @@ export class RecurrenceRuleEditor extends LitElement {
|
||||
this._weekday.clear();
|
||||
this._computeWeekday();
|
||||
}
|
||||
e.stopPropagation();
|
||||
}
|
||||
|
||||
private _onMonthlyDetailSelected(e: CustomEvent<{ value: string }>) {
|
||||
const selectedItem = this._monthlyRepeatItems.find(
|
||||
(item) => item.value === e.detail.value
|
||||
);
|
||||
private _onMonthlyDetailSelected(e: CustomEvent<SelectedDetail<number>>) {
|
||||
e.stopPropagation();
|
||||
const selectedItem = this._monthlyRepeatItems[e.detail.index];
|
||||
if (!selectedItem) {
|
||||
return;
|
||||
}
|
||||
@@ -346,8 +395,8 @@ export class RecurrenceRuleEditor extends LitElement {
|
||||
this.requestUpdate("_weekday");
|
||||
}
|
||||
|
||||
private _onEndSelected(e: CustomEvent<{ value: string }>) {
|
||||
const end = e.detail.value as RepeatEnd;
|
||||
private _onEndSelected(e: CustomEvent<SelectedDetail<number>>) {
|
||||
const end = (e.target as HaSelect).value as RepeatEnd;
|
||||
if (end === this._end) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -186,6 +186,7 @@ class PanelClimate extends LitElement {
|
||||
);
|
||||
padding-top: var(--safe-area-inset-top);
|
||||
z-index: 4;
|
||||
transition: box-shadow 200ms linear;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
-webkit-backdrop-filter: var(--app-header-backdrop-filter, none);
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import type { CSSResultGroup, PropertyValues, TemplateResult } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { stopPropagation } from "../../../../../common/dom/stop_propagation";
|
||||
import "../../../../../components/buttons/ha-progress-button";
|
||||
import "../../../../../components/ha-alert";
|
||||
import "../../../../../components/ha-card";
|
||||
import "../../../../../components/ha-list-item";
|
||||
import "../../../../../components/ha-select";
|
||||
import type {
|
||||
HassioAddonDetails,
|
||||
@@ -14,8 +16,8 @@ import type { HassioHardwareAudioDevice } from "../../../../../data/hassio/hardw
|
||||
import { fetchHassioHardwareAudio } from "../../../../../data/hassio/hardware";
|
||||
import { haStyle } from "../../../../../resources/styles";
|
||||
import type { HomeAssistant } from "../../../../../types";
|
||||
import { supervisorAppsStyle } from "../../resources/supervisor-apps-style";
|
||||
import { suggestSupervisorAppRestart } from "../dialogs/suggestSupervisorAppRestart";
|
||||
import { supervisorAppsStyle } from "../../resources/supervisor-apps-style";
|
||||
|
||||
@customElement("supervisor-app-audio")
|
||||
class SupervisorAppAudio extends LitElement {
|
||||
@@ -53,13 +55,19 @@ class SupervisorAppAudio extends LitElement {
|
||||
"ui.panel.config.apps.configuration.audio.input"
|
||||
)}
|
||||
@selected=${this._setInputDevice}
|
||||
@closed=${stopPropagation}
|
||||
fixedMenuPosition
|
||||
naturalMenuWidth
|
||||
.value=${this._selectedInput!}
|
||||
.disabled=${this.disabled}
|
||||
.options=${this._inputDevices.map((item) => ({
|
||||
value: item.device || "",
|
||||
label: item.name,
|
||||
}))}
|
||||
>
|
||||
${this._inputDevices.map(
|
||||
(item) => html`
|
||||
<ha-list-item .value=${item.device || ""}>
|
||||
${item.name}
|
||||
</ha-list-item>
|
||||
`
|
||||
)}
|
||||
</ha-select>`}
|
||||
${this._outputDevices &&
|
||||
html`<ha-select
|
||||
@@ -67,13 +75,19 @@ class SupervisorAppAudio extends LitElement {
|
||||
"ui.panel.config.apps.configuration.audio.output"
|
||||
)}
|
||||
@selected=${this._setOutputDevice}
|
||||
@closed=${stopPropagation}
|
||||
fixedMenuPosition
|
||||
naturalMenuWidth
|
||||
.value=${this._selectedOutput!}
|
||||
.disabled=${this.disabled}
|
||||
.options=${this._outputDevices.map((item) => ({
|
||||
value: item.device || "",
|
||||
label: item.name,
|
||||
}))}
|
||||
>
|
||||
${this._outputDevices.map(
|
||||
(item) => html`
|
||||
<ha-list-item .value=${item.device || ""}
|
||||
>${item.name}</ha-list-item
|
||||
>
|
||||
`
|
||||
)}
|
||||
</ha-select>`}
|
||||
</div>
|
||||
<div class="card-actions">
|
||||
@@ -102,7 +116,6 @@ class SupervisorAppAudio extends LitElement {
|
||||
}
|
||||
ha-select {
|
||||
width: 100%;
|
||||
display: block;
|
||||
}
|
||||
ha-select:last-child {
|
||||
margin-top: var(--ha-space-2);
|
||||
@@ -118,13 +131,13 @@ class SupervisorAppAudio extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
private _setInputDevice(ev: CustomEvent<{ value: string }>): void {
|
||||
const device = ev.detail.value;
|
||||
private _setInputDevice(ev): void {
|
||||
const device = ev.target.value;
|
||||
this._selectedInput = device;
|
||||
}
|
||||
|
||||
private _setOutputDevice(ev: CustomEvent<{ value: string }>): void {
|
||||
const device = ev.detail.value;
|
||||
private _setOutputDevice(ev): void {
|
||||
const device = ev.target.value;
|
||||
this._selectedOutput = device;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,11 +2,13 @@ import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { fireEvent } from "../../../../../common/dom/fire_event";
|
||||
import { stopPropagation } from "../../../../../common/dom/stop_propagation";
|
||||
import { stringCompare } from "../../../../../common/string/compare";
|
||||
import type { LocalizeFunc } from "../../../../../common/translations/localize";
|
||||
import { CONDITION_ICONS } from "../../../../../components/ha-condition-icon";
|
||||
import "../../../../../components/ha-dropdown-item";
|
||||
import "../../../../../components/ha-list-item";
|
||||
import "../../../../../components/ha-select";
|
||||
import type { HaSelect } from "../../../../../components/ha-select";
|
||||
import {
|
||||
DYNAMIC_PREFIX,
|
||||
getValueFromDynamic,
|
||||
@@ -83,47 +85,37 @@ export class HaConditionAction
|
||||
this.action.condition
|
||||
);
|
||||
|
||||
const value =
|
||||
this.action.condition in this._conditionDescriptions
|
||||
? `${DYNAMIC_PREFIX}${this.action.condition}`
|
||||
: this.action.condition;
|
||||
|
||||
let valueLabel = value;
|
||||
|
||||
const items = html`${this._processedTypes(
|
||||
this._conditionDescriptions,
|
||||
this.hass.localize
|
||||
).map(([opt, label, condition]) => {
|
||||
const selected = value === opt;
|
||||
|
||||
if (selected) {
|
||||
valueLabel = label;
|
||||
}
|
||||
|
||||
return html`
|
||||
<ha-dropdown-item .value=${opt} class=${selected ? "selected" : ""}>
|
||||
<ha-condition-icon
|
||||
.hass=${this.hass}
|
||||
slot="icon"
|
||||
.condition=${condition}
|
||||
></ha-condition-icon>
|
||||
${label}
|
||||
</ha-dropdown-item>
|
||||
`;
|
||||
})}`;
|
||||
|
||||
return html`
|
||||
${this.inSidebar || (!this.inSidebar && !this.indent)
|
||||
? html`
|
||||
<ha-select
|
||||
fixedMenuPosition
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.conditions.type_select"
|
||||
)}
|
||||
.disabled=${this.disabled}
|
||||
.value=${valueLabel}
|
||||
.value=${this.action.condition in this._conditionDescriptions
|
||||
? `${DYNAMIC_PREFIX}${this.action.condition}`
|
||||
: this.action.condition}
|
||||
naturalMenuWidth
|
||||
@selected=${this._typeChanged}
|
||||
@closed=${stopPropagation}
|
||||
>
|
||||
${items}
|
||||
${this._processedTypes(
|
||||
this._conditionDescriptions,
|
||||
this.hass.localize
|
||||
).map(
|
||||
([opt, label, condition]) => html`
|
||||
<ha-list-item .value=${opt} graphic="icon">
|
||||
${label}
|
||||
<ha-condition-icon
|
||||
.hass=${this.hass}
|
||||
slot="graphic"
|
||||
.condition=${condition}
|
||||
></ha-condition-icon>
|
||||
</ha-list-item>
|
||||
`
|
||||
)}
|
||||
</ha-select>
|
||||
`
|
||||
: nothing}
|
||||
@@ -200,8 +192,8 @@ export class HaConditionAction
|
||||
});
|
||||
}
|
||||
|
||||
private _typeChanged(ev: CustomEvent<{ value: string }>) {
|
||||
const type = ev.detail.value;
|
||||
private _typeChanged(ev: CustomEvent) {
|
||||
const type = (ev.target as HaSelect).value;
|
||||
|
||||
if (!type) {
|
||||
return;
|
||||
@@ -250,7 +242,6 @@ export class HaConditionAction
|
||||
static styles = css`
|
||||
ha-select {
|
||||
margin-bottom: 24px;
|
||||
display: block;
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import {
|
||||
mdiToggleSwitchOffOutline,
|
||||
mdiTransitConnection,
|
||||
} from "@mdi/js";
|
||||
import { differenceInDays } from "date-fns";
|
||||
import type { UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||
import type { CSSResultGroup, PropertyValues, TemplateResult } from "lit";
|
||||
import { LitElement, css, html, nothing } from "lit";
|
||||
@@ -27,11 +28,14 @@ import { styleMap } from "lit/directives/style-map";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { computeCssColor } from "../../../common/color/compute-color";
|
||||
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
|
||||
import { formatShortDateTimeWithConditionalYear } from "../../../common/datetime/format_date_time";
|
||||
import { relativeTime } from "../../../common/datetime/relative_time";
|
||||
import { storage } from "../../../common/decorators/storage";
|
||||
import type { HASSDomEvent } from "../../../common/dom/fire_event";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import { computeStateName } from "../../../common/entity/compute_state_name";
|
||||
import { navigate } from "../../../common/navigate";
|
||||
import { slugify } from "../../../common/string/slugify";
|
||||
import type { LocalizeFunc } from "../../../common/translations/localize";
|
||||
import {
|
||||
hasRejectedItems,
|
||||
@@ -111,13 +115,6 @@ import { haStyle } from "../../../resources/styles";
|
||||
import type { HomeAssistant, Route, ServiceCallResponse } from "../../../types";
|
||||
import { documentationUrl } from "../../../util/documentation-url";
|
||||
import { turnOnOffEntity } from "../../lovelace/common/entity/turn-on-off-entity";
|
||||
import {
|
||||
getEntityIdHiddenTableColumn,
|
||||
getAreaTableColumn,
|
||||
getCategoryTableColumn,
|
||||
getLabelsTableColumn,
|
||||
getTriggeredAtTableColumn,
|
||||
} from "../common/data-table-columns";
|
||||
import { showAreaRegistryDetailDialog } from "../areas/show-dialog-area-registry-detail";
|
||||
import { showAssignCategoryDialog } from "../category/show-dialog-assign-category";
|
||||
import { showCategoryRegistryDetailDialog } from "../category/show-dialog-category-registry-detail";
|
||||
@@ -137,7 +134,7 @@ type AutomationItem = AutomationEntity & {
|
||||
last_triggered: string | undefined;
|
||||
formatted_state: string;
|
||||
category: string | undefined;
|
||||
label_entries: LabelRegistryEntry[];
|
||||
labels: LabelRegistryEntry[];
|
||||
assistants: string[];
|
||||
assistants_sortable_key: string | undefined;
|
||||
};
|
||||
@@ -272,7 +269,7 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) {
|
||||
category: category
|
||||
? categoryReg?.find((cat) => cat.category_id === category)?.name
|
||||
: undefined,
|
||||
label_entries: (labels || []).map(
|
||||
labels: (labels || []).map(
|
||||
(lbl) => labelReg!.find((label) => label.label_id === lbl)!
|
||||
),
|
||||
assistants,
|
||||
@@ -287,6 +284,7 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) {
|
||||
(
|
||||
narrow: boolean,
|
||||
localize: LocalizeFunc,
|
||||
locale: HomeAssistant["locale"],
|
||||
entitiesToCheck?: any[]
|
||||
): DataTableColumnContainer<AutomationItem> => {
|
||||
const columns: DataTableColumnContainer<AutomationItem> = {
|
||||
@@ -308,7 +306,11 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) {
|
||||
})}
|
||||
></ha-state-icon>`,
|
||||
},
|
||||
entity_id: getEntityIdHiddenTableColumn(),
|
||||
entity_id: {
|
||||
title: "",
|
||||
hidden: true,
|
||||
filterable: true,
|
||||
},
|
||||
name: {
|
||||
title: localize("ui.panel.config.automation.picker.headers.name"),
|
||||
main: true,
|
||||
@@ -317,17 +319,59 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) {
|
||||
direction: "asc",
|
||||
flex: 2,
|
||||
extraTemplate: (automation) =>
|
||||
automation.label_entries.length
|
||||
automation.labels.length
|
||||
? html`<ha-data-table-labels
|
||||
@label-clicked=${narrow ? undefined : this._labelClicked}
|
||||
.labels=${automation.label_entries}
|
||||
.labels=${automation.labels}
|
||||
></ha-data-table-labels>`
|
||||
: nothing,
|
||||
},
|
||||
area: getAreaTableColumn(localize),
|
||||
category: getCategoryTableColumn(localize),
|
||||
labels: getLabelsTableColumn(),
|
||||
last_triggered: getTriggeredAtTableColumn(localize, this.hass),
|
||||
area: {
|
||||
title: localize("ui.panel.config.automation.picker.headers.area"),
|
||||
groupable: true,
|
||||
filterable: true,
|
||||
sortable: true,
|
||||
},
|
||||
category: {
|
||||
title: localize("ui.panel.config.automation.picker.headers.category"),
|
||||
defaultHidden: true,
|
||||
groupable: true,
|
||||
filterable: true,
|
||||
sortable: true,
|
||||
},
|
||||
labels: {
|
||||
title: "",
|
||||
hidden: true,
|
||||
filterable: true,
|
||||
template: (automation) =>
|
||||
automation.labels.map((lbl) => lbl.name).join(" "),
|
||||
},
|
||||
last_triggered: {
|
||||
sortable: true,
|
||||
title: localize("ui.card.automation.last_triggered"),
|
||||
template: (automation) => {
|
||||
if (!automation.last_triggered) {
|
||||
return this.hass.localize("ui.components.relative_time.never");
|
||||
}
|
||||
const date = new Date(automation.last_triggered);
|
||||
const now = new Date();
|
||||
const dayDifference = differenceInDays(now, date);
|
||||
const formattedTime = formatShortDateTimeWithConditionalYear(
|
||||
date,
|
||||
this.hass.locale,
|
||||
this.hass.config
|
||||
);
|
||||
const elementId = "last-triggered-" + slugify(automation.entity_id);
|
||||
return html`
|
||||
${dayDifference > 3
|
||||
? formattedTime
|
||||
: html`
|
||||
<ha-tooltip for=${elementId}>${formattedTime}</ha-tooltip>
|
||||
<span id=${elementId}>${relativeTime(date, locale)}</span>
|
||||
`}
|
||||
`;
|
||||
},
|
||||
},
|
||||
formatted_state: {
|
||||
minWidth: "82px",
|
||||
maxWidth: "82px",
|
||||
@@ -441,7 +485,12 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) {
|
||||
Array.isArray(val) ? val.length : val
|
||||
)
|
||||
).length}
|
||||
.columns=${this._columns(this.narrow, this.hass.localize, automations)}
|
||||
.columns=${this._columns(
|
||||
this.narrow,
|
||||
this.hass.localize,
|
||||
this.hass.locale,
|
||||
automations
|
||||
)}
|
||||
.initialGroupColumn=${this._activeGrouping ?? "category"}
|
||||
.initialCollapsedGroups=${this._activeCollapsed}
|
||||
.initialSorting=${this._activeSorting}
|
||||
|
||||
@@ -4,6 +4,7 @@ import { customElement, property, state } from "lit/decorators";
|
||||
import { fireEvent } from "../../../../../common/dom/fire_event";
|
||||
import { caseInsensitiveStringCompare } from "../../../../../common/string/compare";
|
||||
import "../../../../../components/ha-select";
|
||||
import "../../../../../components/ha-list-item";
|
||||
import type { TagTrigger } from "../../../../../data/automation";
|
||||
import type { Tag } from "../../../../../data/tag";
|
||||
import { fetchTags } from "../../../../../data/tag";
|
||||
@@ -41,11 +42,16 @@ export class HaTagTrigger extends LitElement implements TriggerElement {
|
||||
.disabled=${this.disabled || this._tags.length === 0}
|
||||
.value=${this.trigger.tag_id}
|
||||
@selected=${this._tagChanged}
|
||||
.options=${this._tags.map((tag) => ({
|
||||
value: tag.id,
|
||||
label: tag.name || tag.id,
|
||||
}))}
|
||||
fixedMenuPosition
|
||||
naturalMenuWidth
|
||||
>
|
||||
${this._tags.map(
|
||||
(tag) => html`
|
||||
<ha-list-item .value=${tag.id}>
|
||||
${tag.name || tag.id}
|
||||
</ha-list-item>
|
||||
`
|
||||
)}
|
||||
</ha-select>
|
||||
`;
|
||||
}
|
||||
@@ -60,18 +66,18 @@ export class HaTagTrigger extends LitElement implements TriggerElement {
|
||||
);
|
||||
}
|
||||
|
||||
private _tagChanged(ev: CustomEvent<{ value: string }>) {
|
||||
private _tagChanged(ev) {
|
||||
if (
|
||||
!ev.detail.value ||
|
||||
!ev.target.value ||
|
||||
!this._tags ||
|
||||
this.trigger.tag_id === ev.detail.value
|
||||
this.trigger.tag_id === ev.target.value
|
||||
) {
|
||||
return;
|
||||
}
|
||||
fireEvent(this, "value-changed", {
|
||||
value: {
|
||||
...this.trigger,
|
||||
tag_id: ev.detail.value,
|
||||
tag_id: ev.target.value,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { mdiContentCopy } from "@mdi/js";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { mdiContentCopy } from "@mdi/js";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import { copyToClipboard } from "../../../../common/util/copy-clipboard";
|
||||
import "../../../../components/ha-button";
|
||||
import "../../../../components/ha-card";
|
||||
import "../../../../components/ha-button";
|
||||
import "../../../../components/ha-language-picker";
|
||||
import "../../../../components/ha-list-item";
|
||||
import "../../../../components/ha-select";
|
||||
import "../../../../components/ha-svg-icon";
|
||||
import "../../../../components/ha-switch";
|
||||
@@ -19,8 +19,9 @@ import {
|
||||
} from "../../../../data/cloud/tts";
|
||||
import { showAlertDialog } from "../../../../dialogs/generic/show-dialog-box";
|
||||
import type { HomeAssistant } from "../../../../types";
|
||||
import { showToast } from "../../../../util/toast";
|
||||
import { showTryTtsDialog } from "./show-dialog-cloud-tts-try";
|
||||
import { copyToClipboard } from "../../../../common/util/copy-clipboard";
|
||||
import { showToast } from "../../../../util/toast";
|
||||
|
||||
export const getCloudTtsSupportedVoices = (
|
||||
language: string,
|
||||
@@ -95,11 +96,13 @@ export class CloudTTSPref extends LitElement {
|
||||
.disabled=${this.savingPreferences}
|
||||
.value=${defaultVoice[1]}
|
||||
@selected=${this._handleVoiceChange}
|
||||
.options=${voices.map((voice) => ({
|
||||
value: voice.voiceId,
|
||||
label: voice.voiceName,
|
||||
}))}
|
||||
>
|
||||
${voices.map(
|
||||
(voice) =>
|
||||
html`<ha-list-item .value=${voice.voiceId}>
|
||||
${voice.voiceName}
|
||||
</ha-list-item>`
|
||||
)}
|
||||
</ha-select>
|
||||
</div>
|
||||
</div>
|
||||
@@ -131,6 +134,16 @@ export class CloudTTSPref extends LitElement {
|
||||
`;
|
||||
}
|
||||
|
||||
protected updated(changedProps) {
|
||||
if (
|
||||
changedProps.has("cloudStatus") &&
|
||||
this.cloudStatus?.prefs.tts_default_voice?.[0] !==
|
||||
changedProps.get("cloudStatus")?.prefs.tts_default_voice?.[0]
|
||||
) {
|
||||
this.renderRoot.querySelector("ha-select")?.layoutOptions();
|
||||
}
|
||||
}
|
||||
|
||||
protected willUpdate(changedProps) {
|
||||
super.willUpdate(changedProps);
|
||||
if (!this.hasUpdated) {
|
||||
@@ -182,13 +195,13 @@ export class CloudTTSPref extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
private async _handleVoiceChange(ev: CustomEvent<{ value: string }>) {
|
||||
if (ev.detail.value === this.cloudStatus!.prefs.tts_default_voice[1]) {
|
||||
private async _handleVoiceChange(ev) {
|
||||
if (ev.target.value === this.cloudStatus!.prefs.tts_default_voice[1]) {
|
||||
return;
|
||||
}
|
||||
this.savingPreferences = true;
|
||||
const language = this.cloudStatus!.prefs.tts_default_voice[0];
|
||||
const voice = ev.detail.value;
|
||||
const voice = ev.target.value;
|
||||
|
||||
try {
|
||||
await updateCloudPref(this.hass, {
|
||||
|
||||
@@ -4,13 +4,14 @@ import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { storage } from "../../../../common/decorators/storage";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import { stopPropagation } from "../../../../common/dom/stop_propagation";
|
||||
import { computeStateDomain } from "../../../../common/entity/compute_state_domain";
|
||||
import { computeStateName } from "../../../../common/entity/compute_state_name";
|
||||
import { supportsFeature } from "../../../../common/entity/supports-feature";
|
||||
import "../../../../components/ha-button";
|
||||
import { createCloseHeading } from "../../../../components/ha-dialog";
|
||||
import "../../../../components/ha-list-item";
|
||||
import "../../../../components/ha-select";
|
||||
import type { HaSelectOption } from "../../../../components/ha-select";
|
||||
import "../../../../components/ha-button";
|
||||
import "../../../../components/ha-textarea";
|
||||
import type { HaTextArea } from "../../../../components/ha-textarea";
|
||||
import { showAutomationEditor } from "../../../../data/automation";
|
||||
@@ -59,25 +60,6 @@ export class DialogTryTts extends LitElement {
|
||||
return nothing;
|
||||
}
|
||||
const target = this._target || "browser";
|
||||
|
||||
const targetOptions: HaSelectOption[] = Object.values(this.hass.states)
|
||||
.filter(
|
||||
(entity) =>
|
||||
computeStateDomain(entity) === "media_player" &&
|
||||
supportsFeature(entity, MediaPlayerEntityFeature.PLAY_MEDIA)
|
||||
)
|
||||
.map((entity) => ({
|
||||
value: entity.entity_id,
|
||||
label: computeStateName(entity),
|
||||
}));
|
||||
|
||||
targetOptions.unshift({
|
||||
value: "browser",
|
||||
label: this.hass.localize(
|
||||
"ui.panel.config.cloud.account.tts.dialog.target_browser"
|
||||
),
|
||||
});
|
||||
|
||||
return html`
|
||||
<ha-dialog
|
||||
open
|
||||
@@ -111,8 +93,28 @@ export class DialogTryTts extends LitElement {
|
||||
id="target"
|
||||
.value=${target}
|
||||
@selected=${this._handleTargetChanged}
|
||||
.options=${targetOptions}
|
||||
fixedMenuPosition
|
||||
naturalMenuWidth
|
||||
@closed=${stopPropagation}
|
||||
>
|
||||
<ha-list-item value="browser">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.cloud.account.tts.dialog.target_browser"
|
||||
)}
|
||||
</ha-list-item>
|
||||
${Object.values(this.hass.states)
|
||||
.filter(
|
||||
(entity) =>
|
||||
computeStateDomain(entity) === "media_player" &&
|
||||
supportsFeature(entity, MediaPlayerEntityFeature.PLAY_MEDIA)
|
||||
)
|
||||
.map(
|
||||
(entity) => html`
|
||||
<ha-list-item .value=${entity.entity_id}>
|
||||
${computeStateName(entity)}
|
||||
</ha-list-item>
|
||||
`
|
||||
)}
|
||||
</ha-select>
|
||||
</div>
|
||||
<ha-button
|
||||
@@ -138,8 +140,8 @@ export class DialogTryTts extends LitElement {
|
||||
`;
|
||||
}
|
||||
|
||||
private _handleTargetChanged(ev: CustomEvent<{ value: string }>) {
|
||||
this._target = ev.detail.value;
|
||||
private _handleTargetChanged(ev) {
|
||||
this._target = ev.target.value;
|
||||
this.requestUpdate("_target");
|
||||
}
|
||||
|
||||
@@ -225,10 +227,11 @@ export class DialogTryTts extends LitElement {
|
||||
}
|
||||
ha-textarea,
|
||||
ha-select {
|
||||
display: block;
|
||||
margin-top: 8px;
|
||||
width: 100%;
|
||||
}
|
||||
ha-select {
|
||||
margin-top: 8px;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -1,204 +0,0 @@
|
||||
import { html, nothing } from "lit";
|
||||
import { differenceInDays } from "date-fns";
|
||||
import { mdiPencilOff } from "@mdi/js";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import type { LocalizeFunc } from "../../../common/translations/localize";
|
||||
import type { DataTableColumnData } from "../../../components/data-table/ha-data-table";
|
||||
import { slugify } from "../../../common/string/slugify";
|
||||
import { relativeTime } from "../../../common/datetime/relative_time";
|
||||
import { formatShortDateTimeWithConditionalYear } from "../../../common/datetime/format_date_time";
|
||||
import { isUnavailableState } from "../../../data/entity/entity";
|
||||
import "../../../components/ha-tooltip";
|
||||
import "../../../components/ha-svg-icon";
|
||||
|
||||
export function getEntityIdHiddenTableColumn<T>(): DataTableColumnData<T> {
|
||||
return {
|
||||
title: "",
|
||||
hidden: true,
|
||||
filterable: true,
|
||||
};
|
||||
}
|
||||
|
||||
export function getEntityIdTableColumn<T>(
|
||||
localize: LocalizeFunc,
|
||||
defaultHidden?: boolean
|
||||
): DataTableColumnData<T> {
|
||||
return {
|
||||
title: localize("ui.panel.config.generic.headers.entity_id"),
|
||||
defaultHidden: defaultHidden,
|
||||
filterable: true,
|
||||
sortable: true,
|
||||
};
|
||||
}
|
||||
|
||||
export function getDomainTableColumn<T>(
|
||||
localize: LocalizeFunc
|
||||
): DataTableColumnData<T> {
|
||||
return {
|
||||
title: localize("ui.panel.config.generic.headers.domain"),
|
||||
hidden: true,
|
||||
groupable: true,
|
||||
filterable: true,
|
||||
sortable: false,
|
||||
};
|
||||
}
|
||||
|
||||
export function getAreaTableColumn<T>(
|
||||
localize: LocalizeFunc
|
||||
): DataTableColumnData<T> {
|
||||
return {
|
||||
title: localize("ui.panel.config.generic.headers.area"),
|
||||
groupable: true,
|
||||
filterable: true,
|
||||
sortable: true,
|
||||
minWidth: "120px",
|
||||
};
|
||||
}
|
||||
|
||||
export function getFloorTableColumn<T>(
|
||||
localize: LocalizeFunc
|
||||
): DataTableColumnData<T> {
|
||||
return {
|
||||
title: localize("ui.panel.config.generic.headers.floor"),
|
||||
defaultHidden: true,
|
||||
groupable: true,
|
||||
filterable: true,
|
||||
sortable: true,
|
||||
minWidth: "120px",
|
||||
};
|
||||
}
|
||||
|
||||
export function getCategoryTableColumn<T>(
|
||||
localize: LocalizeFunc
|
||||
): DataTableColumnData<T> {
|
||||
return {
|
||||
title: localize("ui.panel.config.generic.headers.category"),
|
||||
defaultHidden: true,
|
||||
groupable: true,
|
||||
filterable: true,
|
||||
sortable: true,
|
||||
};
|
||||
}
|
||||
|
||||
export function getEditableTableColumn<T>(
|
||||
localize: LocalizeFunc,
|
||||
tooltip: string
|
||||
): DataTableColumnData<T> {
|
||||
return {
|
||||
title: localize("ui.panel.config.generic.headers.editable"),
|
||||
type: "icon",
|
||||
showNarrow: true,
|
||||
sortable: true,
|
||||
minWidth: "88px",
|
||||
maxWidth: "88px",
|
||||
template: (entry: any) => html`
|
||||
${!entry.editable
|
||||
? html`
|
||||
<ha-svg-icon
|
||||
.id="icon-edit-${slugify(entry.entity_id)}"
|
||||
.path=${mdiPencilOff}
|
||||
style="color: var(--secondary-text-color)"
|
||||
></ha-svg-icon>
|
||||
<ha-tooltip
|
||||
.for="icon-edit-${slugify(entry.entity_id)}"
|
||||
placement="left"
|
||||
>
|
||||
${tooltip}
|
||||
</ha-tooltip>
|
||||
`
|
||||
: nothing}
|
||||
`,
|
||||
};
|
||||
}
|
||||
|
||||
export function getLabelsTableColumn<T>(): DataTableColumnData<T> {
|
||||
return {
|
||||
title: "",
|
||||
hidden: true,
|
||||
filterable: true,
|
||||
template: (entry: any) =>
|
||||
entry.label_entries.map((lbl) => lbl.name).join(" "),
|
||||
};
|
||||
}
|
||||
|
||||
export function getTriggeredAtTableColumn<T>(
|
||||
localize: LocalizeFunc,
|
||||
hass: HomeAssistant
|
||||
): DataTableColumnData<T> {
|
||||
return {
|
||||
title: localize("ui.card.automation.last_triggered"),
|
||||
sortable: true,
|
||||
template: (entry: any) =>
|
||||
renderRelativeTimeColumn(
|
||||
entry.last_triggered,
|
||||
"last-triggered",
|
||||
entry.entity_id,
|
||||
localize,
|
||||
hass
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
export const renderRelativeTimeColumn = (
|
||||
valueRelativeTime: string,
|
||||
valueName: string,
|
||||
entity_id: string,
|
||||
localize: LocalizeFunc,
|
||||
hass: HomeAssistant
|
||||
) => {
|
||||
if (!valueRelativeTime || isUnavailableState(valueRelativeTime)) {
|
||||
return localize("ui.components.relative_time.never");
|
||||
}
|
||||
const date = new Date(valueRelativeTime);
|
||||
const now = new Date();
|
||||
const dayDifference = differenceInDays(now, date);
|
||||
const formattedTime = formatShortDateTimeWithConditionalYear(
|
||||
date,
|
||||
hass.locale,
|
||||
hass.config
|
||||
);
|
||||
const elementId = valueName + "-" + slugify(entity_id);
|
||||
return html`
|
||||
${dayDifference > 3
|
||||
? formattedTime
|
||||
: html`
|
||||
<ha-tooltip for=${elementId}>${formattedTime}</ha-tooltip>
|
||||
<span id=${elementId}>${relativeTime(date, hass.locale)}</span>
|
||||
`}
|
||||
`;
|
||||
};
|
||||
|
||||
export function getCreatedAtTableColumn<T>(
|
||||
localize: LocalizeFunc,
|
||||
hass: HomeAssistant
|
||||
): DataTableColumnData<T> {
|
||||
return {
|
||||
title: localize("ui.panel.config.generic.headers.created_at"),
|
||||
defaultHidden: true,
|
||||
sortable: true,
|
||||
minWidth: "128px",
|
||||
template: (entry: any) => renderDateTimeColumn(entry.created_at, hass),
|
||||
};
|
||||
}
|
||||
|
||||
export function getModifiedAtTableColumn<T>(
|
||||
localize: LocalizeFunc,
|
||||
hass: HomeAssistant
|
||||
): DataTableColumnData<T> {
|
||||
return {
|
||||
title: localize("ui.panel.config.generic.headers.modified_at"),
|
||||
defaultHidden: true,
|
||||
sortable: true,
|
||||
minWidth: "128px",
|
||||
template: (entry: any) => renderDateTimeColumn(entry.modified_at, hass),
|
||||
};
|
||||
}
|
||||
|
||||
const renderDateTimeColumn = (valueDateTime: number, hass: HomeAssistant) =>
|
||||
html`${valueDateTime
|
||||
? formatShortDateTimeWithConditionalYear(
|
||||
new Date(valueDateTime * 1000),
|
||||
hass.locale,
|
||||
hass.config
|
||||
)
|
||||
: nothing}`;
|
||||
@@ -16,6 +16,7 @@ import type { UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { computeCssColor } from "../../../common/color/compute-color";
|
||||
import { formatShortDateTime } from "../../../common/datetime/format_date_time";
|
||||
import { storage } from "../../../common/decorators/storage";
|
||||
import type { HASSDomEvent } from "../../../common/dom/fire_event";
|
||||
import { computeDeviceNameDisplay } from "../../../common/entity/compute_device_name";
|
||||
@@ -95,13 +96,6 @@ import { configSections } from "../ha-panel-config";
|
||||
import "../integrations/ha-integration-overflow-menu";
|
||||
import { showAddIntegrationDialog } from "../integrations/show-add-integration-dialog";
|
||||
import { showLabelDetailDialog } from "../labels/show-dialog-label-detail";
|
||||
import {
|
||||
getAreaTableColumn,
|
||||
getFloorTableColumn,
|
||||
getLabelsTableColumn,
|
||||
getCreatedAtTableColumn,
|
||||
getModifiedAtTableColumn,
|
||||
} from "../common/data-table-columns";
|
||||
import type { HaDropdownSelectEvent } from "../../../components/ha-dropdown";
|
||||
|
||||
interface DeviceRowData extends DeviceRegistryEntry {
|
||||
@@ -109,7 +103,7 @@ interface DeviceRowData extends DeviceRegistryEntry {
|
||||
area?: string;
|
||||
integration?: string;
|
||||
battery_entity?: [string | undefined, string | undefined];
|
||||
label_entries: LabelRegistryEntry[];
|
||||
label_entries: EntityRegistryEntry[];
|
||||
}
|
||||
|
||||
@customElement("ha-config-devices-dashboard")
|
||||
@@ -456,7 +450,7 @@ export class HaConfigDeviceDashboard extends SubscribeMixin(LitElement) {
|
||||
(lbl) => labelReg!.find((label) => label.label_id === lbl)!
|
||||
);
|
||||
|
||||
let floorName;
|
||||
let floorName = "—";
|
||||
if (
|
||||
device.area_id &&
|
||||
areas[device.area_id]?.floor_id &&
|
||||
@@ -562,8 +556,22 @@ export class HaConfigDeviceDashboard extends SubscribeMixin(LitElement) {
|
||||
: nothing}
|
||||
`,
|
||||
},
|
||||
area: getAreaTableColumn(localize),
|
||||
floor: getFloorTableColumn(localize),
|
||||
area: {
|
||||
title: localize("ui.panel.config.devices.data_table.area"),
|
||||
sortable: true,
|
||||
filterable: true,
|
||||
groupable: true,
|
||||
minWidth: "120px",
|
||||
template: (device) => device.area || "—",
|
||||
},
|
||||
floor: {
|
||||
title: localize("ui.panel.config.devices.data_table.floor"),
|
||||
sortable: true,
|
||||
filterable: true,
|
||||
groupable: true,
|
||||
minWidth: "120px",
|
||||
defaultHidden: true,
|
||||
},
|
||||
integration: {
|
||||
title: localize("ui.panel.config.devices.data_table.integration"),
|
||||
sortable: true,
|
||||
@@ -621,8 +629,34 @@ export class HaConfigDeviceDashboard extends SubscribeMixin(LitElement) {
|
||||
: "—";
|
||||
},
|
||||
},
|
||||
created_at: getCreatedAtTableColumn(localize, this.hass),
|
||||
modified_at: getModifiedAtTableColumn(localize, this.hass),
|
||||
created_at: {
|
||||
title: localize("ui.panel.config.generic.headers.created_at"),
|
||||
defaultHidden: true,
|
||||
sortable: true,
|
||||
minWidth: "128px",
|
||||
template: (entry) =>
|
||||
entry.created_at
|
||||
? formatShortDateTime(
|
||||
new Date(entry.created_at * 1000),
|
||||
this.hass.locale,
|
||||
this.hass.config
|
||||
)
|
||||
: "—",
|
||||
},
|
||||
modified_at: {
|
||||
title: localize("ui.panel.config.generic.headers.modified_at"),
|
||||
defaultHidden: true,
|
||||
sortable: true,
|
||||
minWidth: "128px",
|
||||
template: (entry) =>
|
||||
entry.modified_at
|
||||
? formatShortDateTime(
|
||||
new Date(entry.modified_at * 1000),
|
||||
this.hass.locale,
|
||||
this.hass.config
|
||||
)
|
||||
: "—",
|
||||
},
|
||||
disabled_by: {
|
||||
title: localize("ui.panel.config.devices.picker.state"),
|
||||
type: "icon",
|
||||
@@ -651,7 +685,13 @@ export class HaConfigDeviceDashboard extends SubscribeMixin(LitElement) {
|
||||
`
|
||||
: "—",
|
||||
},
|
||||
labels: getLabelsTableColumn(),
|
||||
labels: {
|
||||
title: "",
|
||||
hidden: true,
|
||||
filterable: true,
|
||||
template: (device) =>
|
||||
device.label_entries.map((lbl) => lbl.name).join(" "),
|
||||
},
|
||||
} as DataTableColumnContainer<DeviceItem>;
|
||||
});
|
||||
|
||||
|
||||
@@ -3,12 +3,14 @@ import type { CSSResultGroup } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import { stopPropagation } from "../../../../common/dom/stop_propagation";
|
||||
import "../../../../components/entity/ha-entity-picker";
|
||||
import "../../../../components/entity/ha-statistic-picker";
|
||||
import "../../../../components/ha-button";
|
||||
import "../../../../components/ha-dialog";
|
||||
import "../../../../components/ha-radio";
|
||||
import "../../../../components/ha-button";
|
||||
import "../../../../components/ha-select";
|
||||
import "../../../../components/ha-list-item";
|
||||
import type { DeviceConsumptionEnergyPreference } from "../../../../data/energy";
|
||||
import { energyStatisticHelpUrl } from "../../../../data/energy";
|
||||
import { getStatisticLabel } from "../../../../data/recorder";
|
||||
@@ -93,27 +95,6 @@ export class DialogEnergyDeviceSettingsWater
|
||||
|
||||
const pickableUnit = this._volume_units?.join(", ") || "";
|
||||
|
||||
const includedInDeviceOptions = !this._possibleParents.length
|
||||
? [
|
||||
{
|
||||
value: "-",
|
||||
disabled: true,
|
||||
label: this.hass.localize(
|
||||
"ui.panel.config.energy.device_consumption_water.dialog.no_upstream_devices"
|
||||
),
|
||||
},
|
||||
]
|
||||
: this._possibleParents.map((stat) => ({
|
||||
value: stat.stat_consumption,
|
||||
label:
|
||||
stat.name ||
|
||||
getStatisticLabel(
|
||||
this.hass,
|
||||
stat.stat_consumption,
|
||||
this._params?.statsMetadata?.[stat.stat_consumption]
|
||||
),
|
||||
}));
|
||||
|
||||
return html`
|
||||
<ha-dialog
|
||||
open
|
||||
@@ -175,9 +156,31 @@ export class DialogEnergyDeviceSettingsWater
|
||||
)}
|
||||
.disabled=${!this._device}
|
||||
@selected=${this._parentSelected}
|
||||
@closed=${stopPropagation}
|
||||
fixedMenuPosition
|
||||
naturalMenuWidth
|
||||
clearable
|
||||
.options=${includedInDeviceOptions}
|
||||
>
|
||||
${!this._possibleParents.length
|
||||
? html`
|
||||
<ha-list-item disabled value="-"
|
||||
>${this.hass.localize(
|
||||
"ui.panel.config.energy.device_consumption_water.dialog.no_upstream_devices"
|
||||
)}</ha-list-item
|
||||
>
|
||||
`
|
||||
: this._possibleParents.map(
|
||||
(stat) => html`
|
||||
<ha-list-item .value=${stat.stat_consumption}
|
||||
>${stat.name ||
|
||||
getStatisticLabel(
|
||||
this.hass,
|
||||
stat.stat_consumption,
|
||||
this._params?.statsMetadata?.[stat.stat_consumption]
|
||||
)}</ha-list-item
|
||||
>
|
||||
`
|
||||
)}
|
||||
</ha-select>
|
||||
|
||||
<ha-button
|
||||
@@ -218,10 +221,10 @@ export class DialogEnergyDeviceSettingsWater
|
||||
this._device = newDevice;
|
||||
}
|
||||
|
||||
private _parentSelected(ev: CustomEvent<{ value: string }>) {
|
||||
private _parentSelected(ev) {
|
||||
const newDevice = {
|
||||
...this._device!,
|
||||
included_in_stat: ev.detail.value,
|
||||
included_in_stat: ev.target!.value,
|
||||
} as DeviceConsumptionEnergyPreference;
|
||||
if (!newDevice.included_in_stat) {
|
||||
delete newDevice.included_in_stat;
|
||||
@@ -246,7 +249,6 @@ export class DialogEnergyDeviceSettingsWater
|
||||
width: 100%;
|
||||
}
|
||||
ha-select {
|
||||
display: block;
|
||||
margin-top: 16px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@@ -3,13 +3,14 @@ import type { CSSResultGroup } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import { stopPropagation } from "../../../../common/dom/stop_propagation";
|
||||
import "../../../../components/entity/ha-entity-picker";
|
||||
import "../../../../components/entity/ha-statistic-picker";
|
||||
import "../../../../components/ha-button";
|
||||
import "../../../../components/ha-dialog";
|
||||
import "../../../../components/ha-radio";
|
||||
import "../../../../components/ha-button";
|
||||
import "../../../../components/ha-select";
|
||||
import type { HaSelectOption } from "../../../../components/ha-select";
|
||||
import "../../../../components/ha-list-item";
|
||||
import type { DeviceConsumptionEnergyPreference } from "../../../../data/energy";
|
||||
import { energyStatisticHelpUrl } from "../../../../data/energy";
|
||||
import { getStatisticLabel } from "../../../../data/recorder";
|
||||
@@ -103,28 +104,6 @@ export class DialogEnergyDeviceSettings
|
||||
return nothing;
|
||||
}
|
||||
|
||||
const includedInDeviceOptions: HaSelectOption[] = this._possibleParents
|
||||
.length
|
||||
? this._possibleParents.map((stat) => ({
|
||||
value: stat.stat_consumption,
|
||||
label:
|
||||
stat.name ||
|
||||
getStatisticLabel(
|
||||
this.hass,
|
||||
stat.stat_consumption,
|
||||
this._params?.statsMetadata?.[stat.stat_consumption]
|
||||
),
|
||||
}))
|
||||
: [
|
||||
{
|
||||
value: "-",
|
||||
disabled: true,
|
||||
label: this.hass.localize(
|
||||
"ui.panel.config.energy.device_consumption.dialog.no_upstream_devices"
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
return html`
|
||||
<ha-dialog
|
||||
open
|
||||
@@ -199,9 +178,31 @@ export class DialogEnergyDeviceSettings
|
||||
)}
|
||||
.disabled=${!this._device}
|
||||
@selected=${this._parentSelected}
|
||||
@closed=${stopPropagation}
|
||||
fixedMenuPosition
|
||||
naturalMenuWidth
|
||||
clearable
|
||||
.options=${includedInDeviceOptions}
|
||||
>
|
||||
${!this._possibleParents.length
|
||||
? html`
|
||||
<ha-list-item disabled value="-"
|
||||
>${this.hass.localize(
|
||||
"ui.panel.config.energy.device_consumption.dialog.no_upstream_devices"
|
||||
)}</ha-list-item
|
||||
>
|
||||
`
|
||||
: this._possibleParents.map(
|
||||
(stat) => html`
|
||||
<ha-list-item .value=${stat.stat_consumption}
|
||||
>${stat.name ||
|
||||
getStatisticLabel(
|
||||
this.hass,
|
||||
stat.stat_consumption,
|
||||
this._params?.statsMetadata?.[stat.stat_consumption]
|
||||
)}</ha-list-item
|
||||
>
|
||||
`
|
||||
)}
|
||||
</ha-select>
|
||||
|
||||
<ha-button
|
||||
@@ -256,10 +257,10 @@ export class DialogEnergyDeviceSettings
|
||||
this._device = newDevice;
|
||||
}
|
||||
|
||||
private _parentSelected(ev: CustomEvent<{ value: string }>) {
|
||||
private _parentSelected(ev) {
|
||||
const newDevice = {
|
||||
...this._device!,
|
||||
included_in_stat: ev.detail.value,
|
||||
included_in_stat: ev.target!.value,
|
||||
} as DeviceConsumptionEnergyPreference;
|
||||
if (!newDevice.included_in_stat) {
|
||||
delete newDevice.included_in_stat;
|
||||
@@ -288,7 +289,6 @@ export class DialogEnergyDeviceSettings
|
||||
width: 100%;
|
||||
}
|
||||
ha-select {
|
||||
display: block;
|
||||
margin-top: var(--ha-space-4);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import "@home-assistant/webawesome/dist/components/divider/divider";
|
||||
import { mdiContentCopy, mdiRestore } from "@mdi/js";
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import type { CSSResultGroup, PropertyValues } from "lit";
|
||||
@@ -8,6 +7,7 @@ import { until } from "lit/directives/until";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import { stopPropagation } from "../../../common/dom/stop_propagation";
|
||||
import { computeDomain } from "../../../common/entity/compute_domain";
|
||||
import { computeObjectId } from "../../../common/entity/compute_object_id";
|
||||
import { supportsFeature } from "../../../common/entity/supports-feature";
|
||||
@@ -22,7 +22,6 @@ import { copyToClipboard } from "../../../common/util/copy-clipboard";
|
||||
import "../../../components/ha-alert";
|
||||
import "../../../components/ha-area-picker";
|
||||
import "../../../components/ha-color-picker";
|
||||
import "../../../components/ha-dropdown-item";
|
||||
import "../../../components/ha-icon";
|
||||
import "../../../components/ha-icon-button-next";
|
||||
import "../../../components/ha-icon-picker";
|
||||
@@ -425,46 +424,34 @@ export class EntityRegistrySettingsEditor extends LitElement {
|
||||
.label=${this.hass.localize(
|
||||
"ui.dialogs.entity_registry.editor.device_class"
|
||||
)}
|
||||
naturalMenuWidth
|
||||
fixedMenuPosition
|
||||
@selected=${this._switchAsDomainChanged}
|
||||
value=${this._switchAsLabel(
|
||||
this._switchAsDomain,
|
||||
this._deviceClass
|
||||
)}
|
||||
@closed=${stopPropagation}
|
||||
>
|
||||
<ha-dropdown-item
|
||||
<ha-list-item
|
||||
value="switch"
|
||||
class=${this._switchAsDomain === "switch" &&
|
||||
(!this._deviceClass || this._deviceClass === "switch")
|
||||
? "selected"
|
||||
: ""}
|
||||
.selected=${!this._deviceClass || this._deviceClass === "switch"}
|
||||
>
|
||||
${domainToName(this.hass.localize, "switch")}
|
||||
</ha-dropdown-item>
|
||||
<ha-dropdown-item
|
||||
</ha-list-item>
|
||||
<ha-list-item
|
||||
value="outlet"
|
||||
class=${this._switchAsDomain === "switch" &&
|
||||
this._deviceClass === "outlet"
|
||||
? "selected"
|
||||
: ""}
|
||||
.selected=${this._deviceClass === "outlet"}
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.dialogs.entity_registry.editor.device_classes.switch.outlet"
|
||||
)}
|
||||
</ha-dropdown-item>
|
||||
<wa-divider></wa-divider>
|
||||
</ha-list-item>
|
||||
<li divider role="separator"></li>
|
||||
${this._switchAsDomainsSorted(
|
||||
SWITCH_AS_DOMAINS,
|
||||
this.hass.localize
|
||||
).map(
|
||||
(entry) => html`
|
||||
<ha-dropdown-item
|
||||
.value=${entry.domain}
|
||||
class=${this._switchAsDomain === entry.domain
|
||||
? "selected"
|
||||
: ""}
|
||||
>
|
||||
<ha-list-item .value=${entry.domain}>
|
||||
${entry.label}
|
||||
</ha-dropdown-item>
|
||||
</ha-list-item>
|
||||
`
|
||||
)}
|
||||
</ha-select>`
|
||||
@@ -473,22 +460,19 @@ export class EntityRegistrySettingsEditor extends LitElement {
|
||||
.label=${this.hass.localize(
|
||||
"ui.dialogs.entity_registry.editor.switch_as_x"
|
||||
)}
|
||||
.value=${this._switchAsLabel(this._switchAsDomain)}
|
||||
.value=${this._switchAsDomain}
|
||||
naturalMenuWidth
|
||||
fixedMenuPosition
|
||||
@selected=${this._switchAsDomainChanged}
|
||||
@closed=${stopPropagation}
|
||||
>
|
||||
<ha-dropdown-item
|
||||
value="switch"
|
||||
class=${this._switchAsDomain === "switch" ? "selected" : ""}
|
||||
>
|
||||
<ha-list-item value="switch">
|
||||
${domainToName(this.hass.localize, "switch")}
|
||||
</ha-dropdown-item>
|
||||
<ha-dropdown-item
|
||||
.value=${domain}
|
||||
class=${this._switchAsDomain === domain ? "selected" : ""}
|
||||
>
|
||||
</ha-list-item>
|
||||
<ha-list-item .value=${domain}>
|
||||
${domainToName(this.hass.localize, domain)}
|
||||
</ha-dropdown-item>
|
||||
<wa-divider></wa-divider>
|
||||
</ha-list-item>
|
||||
<li divider role="separator"></li>
|
||||
${this._switchAsDomainsSorted(
|
||||
SWITCH_AS_DOMAINS,
|
||||
this.hass.localize
|
||||
@@ -496,14 +480,9 @@ export class EntityRegistrySettingsEditor extends LitElement {
|
||||
domain === entry.domain
|
||||
? nothing
|
||||
: html`
|
||||
<ha-dropdown-item
|
||||
.value=${entry.domain}
|
||||
class=${this._switchAsDomain === entry.domain
|
||||
? "selected"
|
||||
: ""}
|
||||
>
|
||||
<ha-list-item .value=${entry.domain}>
|
||||
${entry.label}
|
||||
</ha-dropdown-item>
|
||||
</ha-list-item>
|
||||
`
|
||||
)}
|
||||
</ha-select>
|
||||
@@ -534,13 +513,12 @@ export class EntityRegistrySettingsEditor extends LitElement {
|
||||
.label=${this.hass.localize(
|
||||
"ui.dialogs.entity_registry.editor.device_class"
|
||||
)}
|
||||
.value=${this._deviceClass
|
||||
? this.hass.localize(
|
||||
`ui.dialogs.entity_registry.editor.device_classes.${domain}.${this._deviceClass}`
|
||||
)
|
||||
: undefined}
|
||||
.value=${this._deviceClass}
|
||||
naturalMenuWidth
|
||||
fixedMenuPosition
|
||||
clearable
|
||||
@selected=${this._deviceClassChanged}
|
||||
@closed=${stopPropagation}
|
||||
>
|
||||
${this._deviceClassesSorted(
|
||||
domain,
|
||||
@@ -548,39 +526,29 @@ export class EntityRegistrySettingsEditor extends LitElement {
|
||||
this.hass.localize
|
||||
).map(
|
||||
(entry) => html`
|
||||
<ha-dropdown-item
|
||||
.value=${entry.deviceClass}
|
||||
class=${entry.deviceClass === this._deviceClass
|
||||
? "selected"
|
||||
: ""}
|
||||
>
|
||||
<ha-list-item .value=${entry.deviceClass}>
|
||||
${entry.label}
|
||||
</ha-dropdown-item>
|
||||
</ha-list-item>
|
||||
`
|
||||
)}
|
||||
${this._deviceClassOptions[0].length &&
|
||||
this._deviceClassOptions[1].length
|
||||
? html`<wa-divider></wa-divider>`
|
||||
: nothing}
|
||||
? html`<li divider role="separator"></li>`
|
||||
: ""}
|
||||
${this._deviceClassesSorted(
|
||||
domain,
|
||||
this._deviceClassOptions[1],
|
||||
this.hass.localize
|
||||
).map(
|
||||
(entry) => html`
|
||||
<ha-dropdown-item
|
||||
.value=${entry.deviceClass}
|
||||
class=${entry.deviceClass === this._deviceClass
|
||||
? "selected"
|
||||
: ""}
|
||||
>
|
||||
<ha-list-item .value=${entry.deviceClass}>
|
||||
${entry.label}
|
||||
</ha-dropdown-item>
|
||||
</ha-list-item>
|
||||
`
|
||||
)}
|
||||
</ha-select>
|
||||
`
|
||||
: nothing}
|
||||
: ""}
|
||||
${domain === "number" &&
|
||||
this._deviceClass &&
|
||||
stateObj?.attributes.unit_of_measurement &&
|
||||
@@ -592,14 +560,20 @@ export class EntityRegistrySettingsEditor extends LitElement {
|
||||
.label=${this.hass.localize(
|
||||
"ui.dialogs.entity_registry.editor.unit_of_measurement"
|
||||
)}
|
||||
.value=${this._unit_of_measurement ||
|
||||
stateObj.attributes.unit_of_measurement}
|
||||
.value=${stateObj.attributes.unit_of_measurement}
|
||||
naturalMenuWidth
|
||||
fixedMenuPosition
|
||||
@selected=${this._unitChanged}
|
||||
.options=${this._numberDeviceClassConvertibleUnits}
|
||||
@closed=${stopPropagation}
|
||||
>
|
||||
${this._numberDeviceClassConvertibleUnits.map(
|
||||
(unit: string) => html`
|
||||
<ha-list-item .value=${unit}>${unit}</ha-list-item>
|
||||
`
|
||||
)}
|
||||
</ha-select>
|
||||
`
|
||||
: nothing}
|
||||
: ""}
|
||||
${domain === "lock"
|
||||
? html`
|
||||
<ha-textfield
|
||||
@@ -616,7 +590,7 @@ export class EntityRegistrySettingsEditor extends LitElement {
|
||||
@input=${this._defaultcodeChanged}
|
||||
></ha-textfield>
|
||||
`
|
||||
: nothing}
|
||||
: ""}
|
||||
${domain === "alarm_control_panel"
|
||||
? html`
|
||||
<ha-textfield
|
||||
@@ -629,7 +603,7 @@ export class EntityRegistrySettingsEditor extends LitElement {
|
||||
@input=${this._defaultcodeChanged}
|
||||
></ha-textfield>
|
||||
`
|
||||
: nothing}
|
||||
: ""}
|
||||
${domain === "calendar"
|
||||
? html`
|
||||
<ha-color-picker
|
||||
@@ -642,7 +616,7 @@ export class EntityRegistrySettingsEditor extends LitElement {
|
||||
@value-changed=${this._calendarColorChanged}
|
||||
></ha-color-picker>
|
||||
`
|
||||
: nothing}
|
||||
: ""}
|
||||
${domain === "sensor" &&
|
||||
this._deviceClass &&
|
||||
stateObj?.attributes.unit_of_measurement &&
|
||||
@@ -654,14 +628,20 @@ export class EntityRegistrySettingsEditor extends LitElement {
|
||||
.label=${this.hass.localize(
|
||||
"ui.dialogs.entity_registry.editor.unit_of_measurement"
|
||||
)}
|
||||
.value=${this._unit_of_measurement ||
|
||||
stateObj.attributes.unit_of_measurement}
|
||||
.value=${stateObj.attributes.unit_of_measurement}
|
||||
naturalMenuWidth
|
||||
fixedMenuPosition
|
||||
@selected=${this._unitChanged}
|
||||
.options=${this._sensorDeviceClassConvertibleUnits}
|
||||
@closed=${stopPropagation}
|
||||
>
|
||||
${this._sensorDeviceClassConvertibleUnits.map(
|
||||
(unit: string) => html`
|
||||
<ha-list-item .value=${unit}>${unit}</ha-list-item>
|
||||
`
|
||||
)}
|
||||
</ha-select>
|
||||
`
|
||||
: nothing}
|
||||
: ""}
|
||||
${domain === "sensor" &&
|
||||
// Allow customizing the precision for a sensor with numerical device class,
|
||||
// a unit of measurement or state class
|
||||
@@ -677,78 +657,116 @@ export class EntityRegistrySettingsEditor extends LitElement {
|
||||
.value=${this._precision == null
|
||||
? "default"
|
||||
: this._precision.toString()}
|
||||
naturalMenuWidth
|
||||
fixedMenuPosition
|
||||
@selected=${this._precisionChanged}
|
||||
.options=${[
|
||||
{
|
||||
value: "default",
|
||||
label: this.hass.localize(
|
||||
"ui.dialogs.entity_registry.editor.precision_default",
|
||||
{
|
||||
value: this._precisionLabel(
|
||||
defaultPrecision,
|
||||
stateObj?.state
|
||||
),
|
||||
}
|
||||
),
|
||||
},
|
||||
...PRECISIONS.map((precision) => ({
|
||||
value: precision.toString(),
|
||||
label: this._precisionLabel(precision, stateObj?.state),
|
||||
})),
|
||||
]}
|
||||
@closed=${stopPropagation}
|
||||
>
|
||||
<ha-list-item value="default"
|
||||
>${this.hass.localize(
|
||||
"ui.dialogs.entity_registry.editor.precision_default",
|
||||
{
|
||||
value: this._precisionLabel(
|
||||
defaultPrecision,
|
||||
stateObj?.state
|
||||
),
|
||||
}
|
||||
)}</ha-list-item
|
||||
>
|
||||
${PRECISIONS.map(
|
||||
(precision) => html`
|
||||
<ha-list-item .value=${precision.toString()}>
|
||||
${this._precisionLabel(precision, stateObj?.state)}
|
||||
</ha-list-item>
|
||||
`
|
||||
)}
|
||||
</ha-select>
|
||||
`
|
||||
: nothing}
|
||||
: ""}
|
||||
${domain === "weather"
|
||||
? html`
|
||||
<ha-select
|
||||
.label=${this.hass.localize(
|
||||
"ui.dialogs.entity_registry.editor.precipitation_unit"
|
||||
)}
|
||||
.value=${this._precipitation_unit || undefined}
|
||||
.value=${this._precipitation_unit}
|
||||
naturalMenuWidth
|
||||
fixedMenuPosition
|
||||
@selected=${this._precipitationUnitChanged}
|
||||
.options=${this._weatherConvertibleUnits?.precipitation_unit}
|
||||
@closed=${stopPropagation}
|
||||
>
|
||||
${this._weatherConvertibleUnits?.precipitation_unit.map(
|
||||
(unit: string) => html`
|
||||
<ha-list-item .value=${unit}>${unit}</ha-list-item>
|
||||
`
|
||||
)}
|
||||
</ha-select>
|
||||
<ha-select
|
||||
.label=${this.hass.localize(
|
||||
"ui.dialogs.entity_registry.editor.pressure_unit"
|
||||
)}
|
||||
.value=${this._pressure_unit || undefined}
|
||||
.value=${this._pressure_unit}
|
||||
naturalMenuWidth
|
||||
fixedMenuPosition
|
||||
@selected=${this._pressureUnitChanged}
|
||||
.options=${this._weatherConvertibleUnits?.pressure_unit}
|
||||
@closed=${stopPropagation}
|
||||
>
|
||||
${this._weatherConvertibleUnits?.pressure_unit.map(
|
||||
(unit: string) => html`
|
||||
<ha-list-item .value=${unit}>${unit}</ha-list-item>
|
||||
`
|
||||
)}
|
||||
</ha-select>
|
||||
<ha-select
|
||||
.label=${this.hass.localize(
|
||||
"ui.dialogs.entity_registry.editor.temperature_unit"
|
||||
)}
|
||||
.value=${this._temperature_unit || undefined}
|
||||
.value=${this._temperature_unit}
|
||||
naturalMenuWidth
|
||||
fixedMenuPosition
|
||||
@selected=${this._temperatureUnitChanged}
|
||||
.options=${this._weatherConvertibleUnits?.temperature_unit}
|
||||
@closed=${stopPropagation}
|
||||
>
|
||||
${this._weatherConvertibleUnits?.temperature_unit.map(
|
||||
(unit: string) => html`
|
||||
<ha-list-item .value=${unit}>${unit}</ha-list-item>
|
||||
`
|
||||
)}
|
||||
</ha-select>
|
||||
<ha-select
|
||||
.label=${this.hass.localize(
|
||||
"ui.dialogs.entity_registry.editor.visibility_unit"
|
||||
)}
|
||||
.value=${this._visibility_unit || undefined}
|
||||
.value=${this._visibility_unit}
|
||||
naturalMenuWidth
|
||||
fixedMenuPosition
|
||||
@selected=${this._visibilityUnitChanged}
|
||||
.options=${this._weatherConvertibleUnits?.visibility_unit}
|
||||
@closed=${stopPropagation}
|
||||
>
|
||||
${this._weatherConvertibleUnits?.visibility_unit.map(
|
||||
(unit: string) => html`
|
||||
<ha-list-item .value=${unit}>${unit}</ha-list-item>
|
||||
`
|
||||
)}
|
||||
</ha-select>
|
||||
<ha-select
|
||||
.label=${this.hass.localize(
|
||||
"ui.dialogs.entity_registry.editor.wind_speed_unit"
|
||||
)}
|
||||
.value=${this._wind_speed_unit || undefined}
|
||||
.value=${this._wind_speed_unit}
|
||||
naturalMenuWidth
|
||||
fixedMenuPosition
|
||||
@selected=${this._windSpeedUnitChanged}
|
||||
.options=${this._weatherConvertibleUnits?.wind_speed_unit}
|
||||
@closed=${stopPropagation}
|
||||
>
|
||||
${this._weatherConvertibleUnits?.wind_speed_unit.map(
|
||||
(unit: string) => html`
|
||||
<ha-list-item .value=${unit}>${unit}</ha-list-item>
|
||||
`
|
||||
)}
|
||||
</ha-select>
|
||||
`
|
||||
: nothing}
|
||||
: ""}
|
||||
<ha-textfield
|
||||
class="entityId"
|
||||
.value=${computeObjectId(this._entityId)}
|
||||
@@ -783,7 +801,7 @@ export class EntityRegistrySettingsEditor extends LitElement {
|
||||
.disabled=${this.disabled}
|
||||
@value-changed=${this._areaPicked}
|
||||
></ha-area-picker>`
|
||||
: nothing}
|
||||
: ""}
|
||||
<ha-labels-picker
|
||||
.hass=${this.hass}
|
||||
.value=${this._labels}
|
||||
@@ -825,21 +843,26 @@ export class EntityRegistrySettingsEditor extends LitElement {
|
||||
.label=${this.hass.localize(
|
||||
"ui.dialogs.entity_registry.editor.stream.stream_orientation"
|
||||
)}
|
||||
naturalMenuWidth
|
||||
fixedMenuPosition
|
||||
.disabled=${this.disabled}
|
||||
@selected=${this._handleCameraOrientationChanged}
|
||||
.options=${CAMERA_ORIENTATIONS.map((num) => ({
|
||||
value: num.toString(),
|
||||
label: this.hass.localize(
|
||||
("ui.dialogs.entity_registry.editor.stream.stream_orientation_" +
|
||||
num.toString()) as LocalizeKeys
|
||||
),
|
||||
}))}
|
||||
.value=${this._cameraPrefs.orientation.toString()}
|
||||
@closed=${stopPropagation}
|
||||
>
|
||||
${CAMERA_ORIENTATIONS.map((num) => {
|
||||
const localizeStr =
|
||||
"ui.dialogs.entity_registry.editor.stream.stream_orientation_" +
|
||||
num.toString();
|
||||
return html`
|
||||
<ha-list-item value=${num}>
|
||||
${this.hass.localize(localizeStr as LocalizeKeys)}
|
||||
</ha-list-item>
|
||||
`;
|
||||
})}
|
||||
</ha-select>
|
||||
</ha-settings-row>
|
||||
`
|
||||
: nothing}
|
||||
: ""}
|
||||
${this.helperConfigEntry &&
|
||||
this.helperConfigEntry.supports_options &&
|
||||
this.helperConfigEntry.domain !== "switch_as_x"
|
||||
@@ -876,7 +899,7 @@ export class EntityRegistrySettingsEditor extends LitElement {
|
||||
<ha-icon-next slot="meta"></ha-icon-next>
|
||||
</ha-list-item>
|
||||
`
|
||||
: nothing}
|
||||
: ""}
|
||||
|
||||
<ha-list-item
|
||||
class="menu-item"
|
||||
@@ -915,7 +938,7 @@ export class EntityRegistrySettingsEditor extends LitElement {
|
||||
}
|
||||
)}</ha-alert
|
||||
>`
|
||||
: nothing}
|
||||
: ""}
|
||||
|
||||
<ha-settings-row>
|
||||
<span slot="heading"
|
||||
@@ -950,7 +973,7 @@ export class EntityRegistrySettingsEditor extends LitElement {
|
||||
}
|
||||
)}</ha-alert
|
||||
>`
|
||||
: nothing}
|
||||
: ""}
|
||||
|
||||
<ha-settings-row>
|
||||
<span slot="heading"
|
||||
@@ -982,7 +1005,7 @@ export class EntityRegistrySettingsEditor extends LitElement {
|
||||
this.hass.devices[this.entry.device_id].area_id!
|
||||
]?.name
|
||||
})`
|
||||
: nothing}</span
|
||||
: ""}</span
|
||||
>
|
||||
<span slot="description"
|
||||
>${this.hass.localize(
|
||||
@@ -1013,8 +1036,8 @@ export class EntityRegistrySettingsEditor extends LitElement {
|
||||
.disabled=${this.disabled}
|
||||
@value-changed=${this._areaPicked}
|
||||
></ha-area-picker>`
|
||||
: nothing} `
|
||||
: nothing}
|
||||
: ""} `
|
||||
: ""}
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -1320,14 +1343,14 @@ export class EntityRegistrySettingsEditor extends LitElement {
|
||||
this._entityId = `${computeDomain(this._origEntityId)}.${ev.target.value}`;
|
||||
}
|
||||
|
||||
private _deviceClassChanged(ev: CustomEvent<{ value: string }>): void {
|
||||
private _deviceClassChanged(ev): void {
|
||||
fireEvent(this, "change");
|
||||
this._deviceClass = ev.detail.value;
|
||||
this._deviceClass = ev.target.value;
|
||||
}
|
||||
|
||||
private _unitChanged(ev: CustomEvent<{ value: string }>): void {
|
||||
private _unitChanged(ev): void {
|
||||
fireEvent(this, "change");
|
||||
this._unit_of_measurement = ev.detail.value;
|
||||
this._unit_of_measurement = ev.target.value;
|
||||
}
|
||||
|
||||
private _defaultcodeChanged(ev): void {
|
||||
@@ -1340,53 +1363,53 @@ export class EntityRegistrySettingsEditor extends LitElement {
|
||||
this._calendarColor = ev.detail.value || null;
|
||||
}
|
||||
|
||||
private _precipitationUnitChanged(ev: CustomEvent<{ value: string }>): void {
|
||||
private _precipitationUnitChanged(ev): void {
|
||||
fireEvent(this, "change");
|
||||
this._precipitation_unit = ev.detail.value;
|
||||
this._precipitation_unit = ev.target.value;
|
||||
}
|
||||
|
||||
private _precisionChanged(ev: CustomEvent<{ value: string }>): void {
|
||||
private _precisionChanged(ev): void {
|
||||
fireEvent(this, "change");
|
||||
this._precision =
|
||||
ev.detail.value === "default" ? null : Number(ev.detail.value);
|
||||
ev.target.value === "default" ? null : Number(ev.target.value);
|
||||
}
|
||||
|
||||
private _pressureUnitChanged(ev: CustomEvent<{ value: string }>): void {
|
||||
private _pressureUnitChanged(ev): void {
|
||||
fireEvent(this, "change");
|
||||
this._pressure_unit = ev.detail.value;
|
||||
this._pressure_unit = ev.target.value;
|
||||
}
|
||||
|
||||
private _temperatureUnitChanged(ev: CustomEvent<{ value: string }>): void {
|
||||
private _temperatureUnitChanged(ev): void {
|
||||
fireEvent(this, "change");
|
||||
this._temperature_unit = ev.detail.value;
|
||||
this._temperature_unit = ev.target.value;
|
||||
}
|
||||
|
||||
private _visibilityUnitChanged(ev: CustomEvent<{ value: string }>): void {
|
||||
private _visibilityUnitChanged(ev): void {
|
||||
fireEvent(this, "change");
|
||||
this._visibility_unit = ev.detail.value;
|
||||
this._visibility_unit = ev.target.value;
|
||||
}
|
||||
|
||||
private _windSpeedUnitChanged(ev: CustomEvent<{ value: string }>): void {
|
||||
private _windSpeedUnitChanged(ev): void {
|
||||
fireEvent(this, "change");
|
||||
this._wind_speed_unit = ev.detail.value;
|
||||
this._wind_speed_unit = ev.target.value;
|
||||
}
|
||||
|
||||
private _switchAsDomainChanged(ev: CustomEvent<{ value: string }>): void {
|
||||
const value = ev.detail.value;
|
||||
if (value === "") {
|
||||
private _switchAsDomainChanged(ev): void {
|
||||
if (ev.target.value === "") {
|
||||
return;
|
||||
}
|
||||
|
||||
// If value is "outlet" that means the user kept the "switch" domain, but actually changed
|
||||
// the device_class of the switch to "outlet".
|
||||
this._switchAsDomain = value === "outlet" ? "switch" : value;
|
||||
const switchAs = ev.target.value === "outlet" ? "switch" : ev.target.value;
|
||||
this._switchAsDomain = switchAs;
|
||||
|
||||
if (
|
||||
(computeDomain(this.entry.entity_id) === "switch" &&
|
||||
value === "outlet") ||
|
||||
value === "switch"
|
||||
ev.target.value === "outlet") ||
|
||||
ev.target.value === "switch"
|
||||
) {
|
||||
this._deviceClass = value;
|
||||
this._deviceClass = ev.target.value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1443,15 +1466,13 @@ export class EntityRegistrySettingsEditor extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
private async _handleCameraOrientationChanged(
|
||||
ev: CustomEvent<{ value: string }>
|
||||
) {
|
||||
private async _handleCameraOrientationChanged(ev) {
|
||||
try {
|
||||
this._cameraPrefs = await updateCameraPrefs(
|
||||
this.hass,
|
||||
this.entry.entity_id,
|
||||
{
|
||||
orientation: Number(ev.detail.value),
|
||||
orientation: ev.currentTarget.value,
|
||||
}
|
||||
);
|
||||
} catch (err: any) {
|
||||
@@ -1526,35 +1547,6 @@ export class EntityRegistrySettingsEditor extends LitElement {
|
||||
)
|
||||
);
|
||||
|
||||
private _switchAsLabel = memoizeOne(
|
||||
(switchAsDomain: string, deviceClass?: string) => {
|
||||
if (switchAsDomain !== "switch") {
|
||||
const switchAsDomains = this._switchAsDomainsSorted(
|
||||
SWITCH_AS_DOMAINS,
|
||||
this.hass.localize
|
||||
);
|
||||
|
||||
for (const entry of switchAsDomains) {
|
||||
if (entry.domain === switchAsDomain) {
|
||||
return entry.label;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!deviceClass || deviceClass === "switch") {
|
||||
return domainToName(this.hass.localize, "switch");
|
||||
}
|
||||
|
||||
if (deviceClass === "outlet") {
|
||||
return this.hass.localize(
|
||||
"ui.dialogs.entity_registry.editor.device_classes.switch.outlet"
|
||||
);
|
||||
}
|
||||
|
||||
return switchAsDomain;
|
||||
}
|
||||
);
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return [
|
||||
haStyle,
|
||||
@@ -1600,6 +1592,9 @@ export class EntityRegistrySettingsEditor extends LitElement {
|
||||
margin: var(--ha-space-2) 0;
|
||||
width: 100%;
|
||||
}
|
||||
li[divider] {
|
||||
border-bottom-color: var(--divider-color);
|
||||
}
|
||||
.menu-item {
|
||||
border-radius: var(--ha-border-radius-sm);
|
||||
margin-top: 3px;
|
||||
|
||||
@@ -24,6 +24,7 @@ import { ifDefined } from "lit/directives/if-defined";
|
||||
import { styleMap } from "lit/directives/style-map";
|
||||
import memoize from "memoize-one";
|
||||
import { computeCssColor } from "../../../common/color/compute-color";
|
||||
import { formatShortDateTimeWithConditionalYear } from "../../../common/datetime/format_date_time";
|
||||
import { storage } from "../../../common/decorators/storage";
|
||||
import type { HASSDomEvent } from "../../../common/dom/fire_event";
|
||||
import { computeAreaName } from "../../../common/entity/compute_area_name";
|
||||
@@ -119,14 +120,6 @@ import { isHelperDomain } from "../helpers/const";
|
||||
import "../integrations/ha-integration-overflow-menu";
|
||||
import { showAddIntegrationDialog } from "../integrations/show-add-integration-dialog";
|
||||
import { showLabelDetailDialog } from "../labels/show-dialog-label-detail";
|
||||
import {
|
||||
getEntityIdTableColumn,
|
||||
getDomainTableColumn,
|
||||
getAreaTableColumn,
|
||||
getLabelsTableColumn,
|
||||
getCreatedAtTableColumn,
|
||||
getModifiedAtTableColumn,
|
||||
} from "../common/data-table-columns";
|
||||
import {
|
||||
getAssistantsSortableKey,
|
||||
getAssistantsTableColumn,
|
||||
@@ -383,15 +376,32 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
|
||||
groupable: true,
|
||||
hidden: true,
|
||||
},
|
||||
area: getAreaTableColumn(localize),
|
||||
entity_id: getEntityIdTableColumn(localize, true),
|
||||
area: {
|
||||
title: localize("ui.panel.config.entities.picker.headers.area"),
|
||||
sortable: true,
|
||||
filterable: true,
|
||||
groupable: true,
|
||||
template: (entry) => entry.area || "—",
|
||||
},
|
||||
entity_id: {
|
||||
title: localize("ui.panel.config.entities.picker.headers.entity_id"),
|
||||
sortable: true,
|
||||
filterable: true,
|
||||
defaultHidden: true,
|
||||
},
|
||||
localized_platform: {
|
||||
title: localize("ui.panel.config.entities.picker.headers.integration"),
|
||||
sortable: true,
|
||||
groupable: true,
|
||||
filterable: true,
|
||||
},
|
||||
domain: getDomainTableColumn(localize),
|
||||
domain: {
|
||||
title: localize("ui.panel.config.entities.picker.headers.domain"),
|
||||
sortable: false,
|
||||
hidden: true,
|
||||
filterable: true,
|
||||
groupable: true,
|
||||
},
|
||||
disabled_by: {
|
||||
title: localize("ui.panel.config.entities.picker.headers.disabled_by"),
|
||||
hidden: true,
|
||||
@@ -465,8 +475,34 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
|
||||
`
|
||||
: "—",
|
||||
},
|
||||
created_at: getCreatedAtTableColumn(localize, this.hass),
|
||||
modified_at: getModifiedAtTableColumn(localize, this.hass),
|
||||
created_at: {
|
||||
title: localize("ui.panel.config.generic.headers.created_at"),
|
||||
defaultHidden: true,
|
||||
sortable: true,
|
||||
minWidth: "128px",
|
||||
template: (entry) =>
|
||||
entry.created_at
|
||||
? formatShortDateTimeWithConditionalYear(
|
||||
new Date(entry.created_at * 1000),
|
||||
this.hass.locale,
|
||||
this.hass.config
|
||||
)
|
||||
: "—",
|
||||
},
|
||||
modified_at: {
|
||||
title: localize("ui.panel.config.generic.headers.modified_at"),
|
||||
defaultHidden: true,
|
||||
sortable: true,
|
||||
minWidth: "128px",
|
||||
template: (entry) =>
|
||||
entry.modified_at
|
||||
? formatShortDateTimeWithConditionalYear(
|
||||
new Date(entry.modified_at * 1000),
|
||||
this.hass.locale,
|
||||
this.hass.config
|
||||
)
|
||||
: "—",
|
||||
},
|
||||
available: {
|
||||
title: localize("ui.panel.config.entities.picker.headers.availability"),
|
||||
sortable: true,
|
||||
@@ -485,7 +521,13 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
|
||||
groupable: true,
|
||||
hidden: true,
|
||||
},
|
||||
labels: getLabelsTableColumn(),
|
||||
labels: {
|
||||
title: "",
|
||||
hidden: true,
|
||||
filterable: true,
|
||||
template: (entry) =>
|
||||
entry.label_entries.map((lbl) => lbl.name).join(" "),
|
||||
},
|
||||
assistants: getAssistantsTableColumn(
|
||||
localize,
|
||||
this.hass,
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
mdiDotsVertical,
|
||||
mdiDownload,
|
||||
mdiMenuDown,
|
||||
mdiPencilOff,
|
||||
mdiPlus,
|
||||
mdiProgressHelper,
|
||||
mdiTag,
|
||||
@@ -26,6 +27,7 @@ import type { HASSDomEvent } from "../../../common/dom/fire_event";
|
||||
import { computeAreaName } from "../../../common/entity/compute_area_name";
|
||||
import { computeStateDomain } from "../../../common/entity/compute_state_domain";
|
||||
import { navigate } from "../../../common/navigate";
|
||||
import { slugify } from "../../../common/string/slugify";
|
||||
import type {
|
||||
LocalizeFunc,
|
||||
LocalizeKeys,
|
||||
@@ -116,13 +118,6 @@ import { SubscribeMixin } from "../../../mixins/subscribe-mixin";
|
||||
import { haStyle } from "../../../resources/styles";
|
||||
import type { HomeAssistant, Route } from "../../../types";
|
||||
import { fileDownload } from "../../../util/file_download";
|
||||
import {
|
||||
getEntityIdTableColumn,
|
||||
getAreaTableColumn,
|
||||
getCategoryTableColumn,
|
||||
getLabelsTableColumn,
|
||||
getEditableTableColumn,
|
||||
} from "../common/data-table-columns";
|
||||
import { showAssignCategoryDialog } from "../category/show-dialog-assign-category";
|
||||
import { showCategoryRegistryDetailDialog } from "../category/show-dialog-category-registry-detail";
|
||||
import { configSections } from "../ha-panel-config";
|
||||
@@ -365,20 +360,68 @@ export class HaConfigHelpers extends SubscribeMixin(LitElement) {
|
||||
`
|
||||
: nothing,
|
||||
},
|
||||
entity_id: getEntityIdTableColumn(localize),
|
||||
category: getCategoryTableColumn(localize),
|
||||
area: getAreaTableColumn(localize),
|
||||
labels: getLabelsTableColumn(),
|
||||
entity_id: {
|
||||
title: localize("ui.panel.config.helpers.picker.headers.entity_id"),
|
||||
sortable: true,
|
||||
filterable: true,
|
||||
},
|
||||
category: {
|
||||
title: localize("ui.panel.config.helpers.picker.headers.category"),
|
||||
hidden: true,
|
||||
groupable: true,
|
||||
filterable: true,
|
||||
sortable: true,
|
||||
},
|
||||
area: {
|
||||
title: localize("ui.panel.config.helpers.picker.headers.area"),
|
||||
sortable: true,
|
||||
filterable: true,
|
||||
groupable: true,
|
||||
template: (helper) => helper.area || "—",
|
||||
},
|
||||
labels: {
|
||||
title: "",
|
||||
hidden: true,
|
||||
filterable: true,
|
||||
template: (helper) =>
|
||||
helper.label_entries.map((lbl) => lbl.name).join(" "),
|
||||
},
|
||||
localized_type: {
|
||||
title: localize("ui.panel.config.helpers.picker.headers.type"),
|
||||
sortable: true,
|
||||
filterable: true,
|
||||
groupable: true,
|
||||
},
|
||||
editable: getEditableTableColumn(
|
||||
localize,
|
||||
localize("ui.panel.config.entities.picker.status.unmanageable")
|
||||
),
|
||||
editable: {
|
||||
title: localize("ui.panel.config.helpers.picker.headers.editable"),
|
||||
type: "icon",
|
||||
sortable: true,
|
||||
minWidth: "88px",
|
||||
maxWidth: "88px",
|
||||
showNarrow: true,
|
||||
template: (helper) => html`
|
||||
${!helper.editable
|
||||
? html`
|
||||
<div
|
||||
tabindex="0"
|
||||
style="display:inline-block; position: relative;"
|
||||
>
|
||||
<ha-svg-icon
|
||||
.id="icon-edit-${slugify(helper.entity_id)}"
|
||||
.path=${mdiPencilOff}
|
||||
></ha-svg-icon>
|
||||
<ha-tooltip
|
||||
.for="icon-edit-${slugify(helper.entity_id)}"
|
||||
placement="left"
|
||||
>${this.hass.localize(
|
||||
"ui.panel.config.entities.picker.status.unmanageable"
|
||||
)}
|
||||
</ha-tooltip>
|
||||
</div>
|
||||
`
|
||||
: ""}
|
||||
`,
|
||||
},
|
||||
actions: {
|
||||
title: "",
|
||||
label: this.hass.localize("ui.panel.config.generic.headers.actions"),
|
||||
|
||||
@@ -168,7 +168,6 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) {
|
||||
protected willUpdate(changedProperties: PropertyValues): void {
|
||||
if (changedProperties.has("domain")) {
|
||||
this.hass.loadBackendTranslation("title", [this.domain]);
|
||||
this.hass.loadBackendTranslation("config", [this.domain]);
|
||||
this.hass.loadBackendTranslation("config_subentries", [this.domain]);
|
||||
this._extraConfigEntries = undefined;
|
||||
this._fetchManifest();
|
||||
|
||||
@@ -222,6 +222,7 @@ export class BluetoothAdvertisementMonitorPanel extends LitElement {
|
||||
filter=${this.address || ""}
|
||||
clickable
|
||||
.tabs=${bluetoothTabs}
|
||||
back-path="/config"
|
||||
></hass-tabs-subpage-data-table>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -161,6 +161,7 @@ export class BluetoothConfigDashboard extends LitElement {
|
||||
.hass=${this.hass}
|
||||
.route=${this.route}
|
||||
.tabs=${bluetoothTabs}
|
||||
back-path="/config"
|
||||
>
|
||||
<div class="content">
|
||||
<ha-card
|
||||
|
||||
@@ -225,6 +225,7 @@ export class BluetoothConnectionMonitorPanel extends LitElement {
|
||||
)}
|
||||
@grouping-changed=${this._handleGroupingChanged}
|
||||
@collapsed-changed=${this._handleCollapseChanged}
|
||||
back-path="/config"
|
||||
></hass-tabs-subpage-data-table>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -124,6 +124,7 @@ export class BluetoothNetworkVisualization extends LitElement {
|
||||
.narrow=${this.narrow}
|
||||
.route=${this.route}
|
||||
.tabs=${bluetoothTabs}
|
||||
back-path="/config"
|
||||
>
|
||||
<ha-network-graph
|
||||
.hass=${this.hass}
|
||||
|
||||
@@ -2,20 +2,21 @@ import type { CSSResultGroup, TemplateResult } from "lit";
|
||||
import { css, html, LitElement } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { storage } from "../../../../../common/decorators/storage";
|
||||
import "../../../../../components/ha-button";
|
||||
import "../../../../../components/ha-card";
|
||||
import "../../../../../components/ha-code-editor";
|
||||
import "../../../../../components/ha-formfield";
|
||||
import "../../../../../components/ha-list-item";
|
||||
import "../../../../../components/ha-switch";
|
||||
import "../../../../../components/ha-button";
|
||||
import { getConfigEntries } from "../../../../../data/config_entries";
|
||||
import type { Action } from "../../../../../data/script";
|
||||
import { callExecuteScript } from "../../../../../data/service";
|
||||
import { showOptionsFlowDialog } from "../../../../../dialogs/config-flow/show-dialog-options-flow";
|
||||
import "../../../../../layouts/hass-subpage";
|
||||
import { haStyle } from "../../../../../resources/styles";
|
||||
import type { HomeAssistant } from "../../../../../types";
|
||||
import { showToast } from "../../../../../util/toast";
|
||||
import "./mqtt-subscribe-card";
|
||||
import type { Action } from "../../../../../data/script";
|
||||
import { callExecuteScript } from "../../../../../data/service";
|
||||
import { showToast } from "../../../../../util/toast";
|
||||
|
||||
const qosLevel = ["0", "1", "2"];
|
||||
|
||||
@@ -88,8 +89,10 @@ export class MQTTConfigPanel extends LitElement {
|
||||
.label=${this.hass.localize("ui.panel.config.mqtt.qos")}
|
||||
.value=${this._qos}
|
||||
@selected=${this._handleQos}
|
||||
.options=${qosLevel}
|
||||
>
|
||||
>${qosLevel.map(
|
||||
(qos) =>
|
||||
html`<ha-list-item .value=${qos}>${qos}</ha-list-item>`
|
||||
)}
|
||||
</ha-select>
|
||||
<ha-formfield
|
||||
label=${this.hass!.localize("ui.panel.config.mqtt.retain")}
|
||||
@@ -134,9 +137,9 @@ export class MQTTConfigPanel extends LitElement {
|
||||
this._payload = ev.detail.value;
|
||||
}
|
||||
|
||||
private _handleQos(ev: CustomEvent<{ value: string }>) {
|
||||
const newValue = ev.detail.value;
|
||||
if (Number(newValue) >= 0 && newValue !== this._qos) {
|
||||
private _handleQos(ev: CustomEvent) {
|
||||
const newValue = (ev.target! as any).value;
|
||||
if (newValue >= 0 && newValue !== this._qos) {
|
||||
this._qos = newValue;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,8 @@ import type { TemplateResult } from "lit";
|
||||
import { css, html, LitElement } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { formatTime } from "../../../../../common/datetime/format_time";
|
||||
import "../../../../../components/ha-button";
|
||||
import "../../../../../components/ha-card";
|
||||
import "../../../../../components/ha-button";
|
||||
import "../../../../../components/ha-select";
|
||||
import "../../../../../components/ha-textfield";
|
||||
import type { MQTTMessage } from "../../../../../data/mqtt";
|
||||
@@ -12,6 +12,7 @@ import type { HomeAssistant } from "../../../../../types";
|
||||
|
||||
import { storage } from "../../../../../common/decorators/storage";
|
||||
import "../../../../../components/ha-formfield";
|
||||
import "../../../../../components/ha-list-item";
|
||||
import "../../../../../components/ha-switch";
|
||||
|
||||
const qosLevel = ["0", "1", "2"];
|
||||
@@ -95,8 +96,9 @@ class MqttSubscribeCard extends LitElement {
|
||||
.disabled=${this._subscribed !== undefined}
|
||||
.value=${this._qos}
|
||||
@selected=${this._handleQos}
|
||||
.options=${qosLevel}
|
||||
>
|
||||
>${qosLevel.map(
|
||||
(qos) => html`<ha-list-item .value=${qos}>${qos}</ha-list-item>`
|
||||
)}
|
||||
</ha-select>
|
||||
<ha-button
|
||||
appearance="plain"
|
||||
@@ -140,9 +142,9 @@ class MqttSubscribeCard extends LitElement {
|
||||
this._topic = ev.target.value;
|
||||
}
|
||||
|
||||
private _handleQos(ev: CustomEvent<{ value: string }>): void {
|
||||
const newValue = ev.detail.value;
|
||||
if (Number(newValue) >= 0 && newValue !== this._qos) {
|
||||
private _handleQos(ev: CustomEvent): void {
|
||||
const newValue = (ev.target! as any).value;
|
||||
if (newValue >= 0 && newValue !== this._qos) {
|
||||
this._qos = newValue;
|
||||
}
|
||||
}
|
||||
@@ -191,8 +193,8 @@ class MqttSubscribeCard extends LitElement {
|
||||
|
||||
static styles = css`
|
||||
form {
|
||||
padding: var(--ha-space-4);
|
||||
padding-bottom: var(--ha-space-8);
|
||||
display: block;
|
||||
padding: 16px;
|
||||
}
|
||||
.events {
|
||||
margin: -16px 0;
|
||||
@@ -227,7 +229,6 @@ class MqttSubscribeCard extends LitElement {
|
||||
}
|
||||
@media screen and (max-width: 600px) {
|
||||
ha-select {
|
||||
display: block;
|
||||
margin-left: 0px;
|
||||
margin-top: 8px;
|
||||
margin-inline-start: 0px;
|
||||
|
||||
@@ -2,10 +2,12 @@ import type { TemplateResult } from "lit";
|
||||
import { html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { fireEvent } from "../../../../../common/dom/fire_event";
|
||||
import { stopPropagation } from "../../../../../common/dom/stop_propagation";
|
||||
import "../../../../../components/buttons/ha-progress-button";
|
||||
import "../../../../../components/ha-alert";
|
||||
import "../../../../../components/ha-button";
|
||||
import { createCloseHeading } from "../../../../../components/ha-dialog";
|
||||
import "../../../../../components/ha-list-item";
|
||||
import "../../../../../components/ha-select";
|
||||
import { changeZHANetworkChannel } from "../../../../../data/zha";
|
||||
import { showAlertDialog } from "../../../../../dialogs/generic/show-dialog-box";
|
||||
@@ -94,18 +96,22 @@ class DialogZHAChangeChannel extends LitElement implements HassDialog {
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.config.zha.change_channel_dialog.new_channel"
|
||||
)}
|
||||
fixedMenuPosition
|
||||
naturalMenuWidth
|
||||
@selected=${this._newChannelChosen}
|
||||
@closed=${stopPropagation}
|
||||
.value=${String(this._newChannel)}
|
||||
.options=${VALID_CHANNELS.map((channel) => ({
|
||||
value: String(channel),
|
||||
label:
|
||||
channel === "auto"
|
||||
? this.hass.localize(
|
||||
"ui.panel.config.zha.change_channel_dialog.channel_auto"
|
||||
)
|
||||
: String(channel),
|
||||
}))}
|
||||
>
|
||||
${VALID_CHANNELS.map(
|
||||
(newChannel) =>
|
||||
html`<ha-list-item .value=${String(newChannel)}
|
||||
>${newChannel === "auto"
|
||||
? this.hass.localize(
|
||||
"ui.panel.config.zha.change_channel_dialog.channel_auto"
|
||||
)
|
||||
: newChannel}</ha-list-item
|
||||
>`
|
||||
)}
|
||||
</ha-select>
|
||||
</p>
|
||||
|
||||
@@ -131,8 +137,8 @@ class DialogZHAChangeChannel extends LitElement implements HassDialog {
|
||||
`;
|
||||
}
|
||||
|
||||
private _newChannelChosen(ev: CustomEvent<{ value: string }>): void {
|
||||
const value: string = ev.detail.value;
|
||||
private _newChannelChosen(evt: Event): void {
|
||||
const value: string = (evt.target! as HTMLSelectElement).value;
|
||||
this._newChannel = value === "auto" ? "auto" : parseInt(value, 10);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import type { CSSResultGroup, PropertyValues, TemplateResult } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { stopPropagation } from "../../../../../common/dom/stop_propagation";
|
||||
import "../../../../../components/buttons/ha-call-service-button";
|
||||
import "../../../../../components/buttons/ha-progress-button";
|
||||
import "../../../../../components/ha-card";
|
||||
import "../../../../../components/ha-list-item";
|
||||
import "../../../../../components/ha-select";
|
||||
import "../../../../../components/ha-textfield";
|
||||
import { forwardHaptic } from "../../../../../data/haptics";
|
||||
@@ -20,7 +22,7 @@ import {
|
||||
import { haStyle } from "../../../../../resources/styles";
|
||||
import type { HomeAssistant } from "../../../../../types";
|
||||
import { formatAsPaddedHex } from "./functions";
|
||||
import type { SetAttributeServiceData } from "./types";
|
||||
import type { ItemSelectedEvent, SetAttributeServiceData } from "./types";
|
||||
|
||||
@customElement("zha-cluster-attributes")
|
||||
export class ZHAClusterAttributes extends LitElement {
|
||||
@@ -68,16 +70,22 @@ export class ZHAClusterAttributes extends LitElement {
|
||||
class="menu"
|
||||
.value=${String(this._selectedAttributeId)}
|
||||
@selected=${this._selectedAttributeChanged}
|
||||
.options=${this._attributes.map((entry) => ({
|
||||
value: String(entry.id),
|
||||
label: `${entry.name} (id: ${formatAsPaddedHex(entry.id)})`,
|
||||
}))}
|
||||
@closed=${stopPropagation}
|
||||
fixedMenuPosition
|
||||
naturalMenuWidth
|
||||
>
|
||||
${this._attributes.map(
|
||||
(entry) => html`
|
||||
<ha-list-item .value=${String(entry.id)}>
|
||||
${`${entry.name} (id: ${formatAsPaddedHex(entry.id)})`}
|
||||
</ha-list-item>
|
||||
`
|
||||
)}
|
||||
</ha-select>
|
||||
</div>
|
||||
${this._selectedAttributeId !== undefined
|
||||
? this._renderAttributeInteractions()
|
||||
: nothing}
|
||||
: ""}
|
||||
</ha-card>
|
||||
`;
|
||||
}
|
||||
@@ -213,10 +221,8 @@ export class ZHAClusterAttributes extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
private _selectedAttributeChanged(
|
||||
event: CustomEvent<{ value: string }>
|
||||
): void {
|
||||
this._selectedAttributeId = Number(event.detail.value);
|
||||
private _selectedAttributeChanged(event: ItemSelectedEvent): void {
|
||||
this._selectedAttributeId = Number(event.target!.value);
|
||||
this._attributeValue = "";
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import type { CSSResultGroup, PropertyValues } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { stopPropagation } from "../../../../../common/dom/stop_propagation";
|
||||
import "../../../../../components/buttons/ha-call-service-button";
|
||||
import "../../../../../components/ha-card";
|
||||
import "../../../../../components/ha-form/ha-form";
|
||||
import "../../../../../components/ha-list-item";
|
||||
import "../../../../../components/ha-select";
|
||||
import "../../../../../components/ha-textfield";
|
||||
import type { Cluster, Command, ZHADevice } from "../../../../../data/zha";
|
||||
@@ -62,11 +64,17 @@ export class ZHAClusterCommands extends LitElement {
|
||||
class="menu"
|
||||
.value=${String(this._selectedCommandId)}
|
||||
@selected=${this._selectedCommandChanged}
|
||||
.options=${this._commands.map((entry) => ({
|
||||
value: String(entry.id),
|
||||
label: `${entry.name} (id: ${formatAsPaddedHex(entry.id)})`,
|
||||
}))}
|
||||
@closed=${stopPropagation}
|
||||
fixedMenuPosition
|
||||
naturalMenuWidth
|
||||
>
|
||||
${this._commands.map(
|
||||
(entry) => html`
|
||||
<ha-list-item .value=${String(entry.id)}>
|
||||
${entry.name} (id: ${formatAsPaddedHex(entry.id)})
|
||||
</ha-list-item>
|
||||
`
|
||||
)}
|
||||
</ha-select>
|
||||
</div>
|
||||
${this._selectedCommandId !== undefined
|
||||
@@ -171,8 +179,8 @@ export class ZHAClusterCommands extends LitElement {
|
||||
this._computeIssueClusterCommandServiceData();
|
||||
}
|
||||
|
||||
private _selectedCommandChanged(event: CustomEvent<{ value: string }>): void {
|
||||
this._selectedCommandId = Number(event.detail.value);
|
||||
private _selectedCommandChanged(event): void {
|
||||
this._selectedCommandId = Number(event.target.value);
|
||||
this._issueClusterCommandServiceData =
|
||||
this._computeIssueClusterCommandServiceData();
|
||||
}
|
||||
|
||||
@@ -111,6 +111,7 @@ class ZHAConfigDashboard extends LitElement {
|
||||
.narrow=${this.narrow}
|
||||
.route=${this.route}
|
||||
.tabs=${zhaTabs}
|
||||
back-path="/config"
|
||||
has-fab
|
||||
>
|
||||
<div class="container">
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
import type { CSSResultGroup, PropertyValues, TemplateResult } from "lit";
|
||||
import { css, html, LitElement } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { stopPropagation } from "../../../../../common/dom/stop_propagation";
|
||||
import "../../../../../components/buttons/ha-progress-button";
|
||||
import "../../../../../components/ha-card";
|
||||
import "../../../../../components/ha-select";
|
||||
import "../../../../../components/ha-list-item";
|
||||
import type { ZHADevice } from "../../../../../data/zha";
|
||||
import { bindDevices, unbindDevices } from "../../../../../data/zha";
|
||||
import { haStyle } from "../../../../../resources/styles";
|
||||
import type { HomeAssistant } from "../../../../../types";
|
||||
import type { ItemSelectedEvent } from "./types";
|
||||
|
||||
@customElement("zha-device-binding-control")
|
||||
export class ZHADeviceBindingControl extends LitElement {
|
||||
@@ -41,13 +44,19 @@ export class ZHADeviceBindingControl extends LitElement {
|
||||
class="menu"
|
||||
.value=${String(this._bindTargetIndex)}
|
||||
@selected=${this._bindTargetIndexChanged}
|
||||
.options=${this.bindableDevices.map((device, idx) => ({
|
||||
value: String(idx),
|
||||
label: device.user_given_name
|
||||
? device.user_given_name
|
||||
: device.name,
|
||||
}))}
|
||||
@closed=${stopPropagation}
|
||||
fixedMenuPosition
|
||||
naturalMenuWidth
|
||||
>
|
||||
${this.bindableDevices.map(
|
||||
(device, idx) => html`
|
||||
<ha-list-item .value=${String(idx)}>
|
||||
${device.user_given_name
|
||||
? device.user_given_name
|
||||
: device.name}
|
||||
</ha-list-item>
|
||||
`
|
||||
)}
|
||||
</ha-select>
|
||||
</div>
|
||||
<div class="card-actions">
|
||||
@@ -72,8 +81,8 @@ export class ZHADeviceBindingControl extends LitElement {
|
||||
`;
|
||||
}
|
||||
|
||||
private _bindTargetIndexChanged(event: CustomEvent<{ value: string }>): void {
|
||||
this._bindTargetIndex = Number(event.detail.value);
|
||||
private _bindTargetIndexChanged(event: ItemSelectedEvent): void {
|
||||
this._bindTargetIndex = Number(event.target!.value);
|
||||
this._deviceToBind =
|
||||
this._bindTargetIndex === -1
|
||||
? undefined
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import type { CSSResultGroup, PropertyValues, TemplateResult } from "lit";
|
||||
import { css, html, LitElement } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { customElement, property, state, query } from "lit/decorators";
|
||||
import type { HASSDomEvent } from "../../../../../common/dom/fire_event";
|
||||
import { stopPropagation } from "../../../../../common/dom/stop_propagation";
|
||||
import "../../../../../components/buttons/ha-progress-button";
|
||||
import type { SelectionChangedEvent } from "../../../../../components/data-table/ha-data-table";
|
||||
import "../../../../../components/ha-card";
|
||||
import "../../../../../components/ha-select";
|
||||
import "../../../../../components/ha-list-item";
|
||||
import type { Cluster, ZHADevice, ZHAGroup } from "../../../../../data/zha";
|
||||
import {
|
||||
bindDeviceToGroup,
|
||||
@@ -14,6 +16,7 @@ import {
|
||||
} from "../../../../../data/zha";
|
||||
import { haStyle } from "../../../../../resources/styles";
|
||||
import type { HomeAssistant } from "../../../../../types";
|
||||
import type { ItemSelectedEvent } from "./types";
|
||||
import "./zha-clusters-data-table";
|
||||
import type { ZHAClustersDataTable } from "./zha-clusters-data-table";
|
||||
|
||||
@@ -61,11 +64,16 @@ export class ZHAGroupBindingControl extends LitElement {
|
||||
class="menu"
|
||||
.value=${String(this._bindTargetIndex)}
|
||||
@selected=${this._bindTargetIndexChanged}
|
||||
.options=${this.groups.map((group, idx) => ({
|
||||
value: String(idx),
|
||||
label: group.name,
|
||||
}))}
|
||||
@closed=${stopPropagation}
|
||||
fixedMenuPosition
|
||||
naturalMenuWidth
|
||||
>
|
||||
${this.groups.map(
|
||||
(group, idx) =>
|
||||
html`<ha-list-item .value=${String(idx)}
|
||||
>${group.name}</ha-list-item
|
||||
> `
|
||||
)}
|
||||
</ha-select>
|
||||
</div>
|
||||
<div class="command-picker">
|
||||
@@ -101,8 +109,8 @@ export class ZHAGroupBindingControl extends LitElement {
|
||||
`;
|
||||
}
|
||||
|
||||
private _bindTargetIndexChanged(event: CustomEvent<{ value: string }>): void {
|
||||
this._bindTargetIndex = Number(event.detail.value);
|
||||
private _bindTargetIndexChanged(event: ItemSelectedEvent): void {
|
||||
this._bindTargetIndex = Number(event.target!.value);
|
||||
this._groupToBind =
|
||||
this._bindTargetIndex === -1
|
||||
? undefined
|
||||
|
||||
@@ -101,6 +101,7 @@ export class ZHAGroupsDashboard extends LitElement {
|
||||
return html`
|
||||
<hass-tabs-subpage-data-table
|
||||
.tabs=${zhaTabs}
|
||||
back-path="/config"
|
||||
.hass=${this.hass}
|
||||
.narrow=${this.narrow}
|
||||
.route=${this.route}
|
||||
|
||||
@@ -2,7 +2,9 @@ import type { CSSResultGroup, PropertyValues } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { cache } from "lit/directives/cache";
|
||||
import { stopPropagation } from "../../../../../common/dom/stop_propagation";
|
||||
import "../../../../../components/ha-card";
|
||||
import "../../../../../components/ha-list-item";
|
||||
import "../../../../../components/ha-select";
|
||||
import "../../../../../components/ha-tab-group";
|
||||
import "../../../../../components/ha-tab-group-tab";
|
||||
@@ -75,11 +77,17 @@ export class ZHAManageClusters extends LitElement {
|
||||
class="menu"
|
||||
.value=${String(this._selectedClusterIndex)}
|
||||
@selected=${this._selectedClusterChanged}
|
||||
.options=${this._clusters.map((entry, idx) => ({
|
||||
value: String(idx),
|
||||
label: computeClusterKey(entry),
|
||||
}))}
|
||||
@closed=${stopPropagation}
|
||||
fixedMenuPosition
|
||||
naturalMenuWidth
|
||||
>
|
||||
${this._clusters.map(
|
||||
(entry, idx) => html`
|
||||
<ha-list-item .value=${String(idx)}
|
||||
>${computeClusterKey(entry)}</ha-list-item
|
||||
>
|
||||
`
|
||||
)}
|
||||
</ha-select>
|
||||
</div>
|
||||
${this._selectedCluster
|
||||
@@ -147,8 +155,8 @@ export class ZHAManageClusters extends LitElement {
|
||||
this._currTab = newTab;
|
||||
}
|
||||
|
||||
private _selectedClusterChanged(event: CustomEvent<{ value: string }>): void {
|
||||
this._selectedClusterIndex = Number(event.detail.value);
|
||||
private _selectedClusterChanged(event): void {
|
||||
this._selectedClusterIndex = Number(event.target!.value);
|
||||
this._selectedCluster = this._clusters[this._selectedClusterIndex];
|
||||
}
|
||||
|
||||
|
||||
@@ -54,6 +54,7 @@ export class ZHANetworkVisualizationPage extends LitElement {
|
||||
return html`
|
||||
<hass-tabs-subpage
|
||||
.tabs=${zhaTabs}
|
||||
back-path="/config"
|
||||
.hass=${this.hass}
|
||||
.narrow=${this.narrow}
|
||||
.isWide=${this.isWide}
|
||||
|
||||
@@ -5,6 +5,7 @@ import type { HaProgressButton } from "../../../../../../components/buttons/ha-p
|
||||
import "../../../../../../components/ha-alert";
|
||||
import "../../../../../../components/ha-button";
|
||||
import "../../../../../../components/ha-formfield";
|
||||
import "../../../../../../components/ha-list-item";
|
||||
import "../../../../../../components/ha-select";
|
||||
import "../../../../../../components/ha-spinner";
|
||||
import "../../../../../../components/ha-switch";
|
||||
@@ -125,13 +126,16 @@ class ZWaveJSCapabilityDoorLock extends LitElement {
|
||||
)}
|
||||
.value=${this._currentDoorLockMode?.toString() ?? ""}
|
||||
@selected=${this._doorLockModeChanged}
|
||||
.options=${supportedDoorLockModes.map((mode) => ({
|
||||
value: mode.toString(),
|
||||
label: this.hass.localize(
|
||||
`ui.panel.config.zwave_js.node_installer.capability_controls.door_lock.modes.${mode}`
|
||||
),
|
||||
}))}
|
||||
>
|
||||
${supportedDoorLockModes.map(
|
||||
(mode) => html`
|
||||
<ha-list-item .value=${mode.toString()}>
|
||||
${this.hass.localize(
|
||||
`ui.panel.config.zwave_js.node_installer.capability_controls.door_lock.modes.${mode}`
|
||||
)}
|
||||
</ha-list-item>
|
||||
`
|
||||
)}
|
||||
</ha-select>
|
||||
</div>
|
||||
<div class="row">
|
||||
@@ -141,13 +145,16 @@ class ZWaveJSCapabilityDoorLock extends LitElement {
|
||||
)}
|
||||
.value=${this._configuration.operationType.toString()}
|
||||
@selected=${this._operationTypeChanged}
|
||||
.options=${this._capabilities.supportedOperationTypes.map((type) => ({
|
||||
value: type.toString(),
|
||||
label: this.hass.localize(
|
||||
`ui.panel.config.zwave_js.node_installer.capability_controls.door_lock.operation_types.${type}`
|
||||
),
|
||||
}))}
|
||||
>
|
||||
${this._capabilities.supportedOperationTypes.map(
|
||||
(type) => html`
|
||||
<ha-list-item .value=${type.toString()}>
|
||||
${this.hass.localize(
|
||||
`ui.panel.config.zwave_js.node_installer.capability_controls.door_lock.operation_types.${type}`
|
||||
)}
|
||||
</ha-list-item>
|
||||
`
|
||||
)}
|
||||
</ha-select>
|
||||
</div>
|
||||
|
||||
@@ -339,8 +346,9 @@ class ZWaveJSCapabilityDoorLock extends LitElement {
|
||||
);
|
||||
}
|
||||
|
||||
private _operationTypeChanged(ev: CustomEvent<{ value: string }>) {
|
||||
const newType = parseInt(ev.detail.value);
|
||||
private _operationTypeChanged(ev: CustomEvent) {
|
||||
const target = ev.target as HTMLSelectElement;
|
||||
const newType = parseInt(target.value);
|
||||
if (this._configuration) {
|
||||
this._configuration = {
|
||||
...this._configuration,
|
||||
@@ -385,8 +393,9 @@ class ZWaveJSCapabilityDoorLock extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
private _doorLockModeChanged(ev: CustomEvent<{ value: string }>) {
|
||||
this._currentDoorLockMode = parseInt(ev.detail.value) as DoorLockMode;
|
||||
private _doorLockModeChanged(ev: CustomEvent) {
|
||||
const target = ev.target as HTMLSelectElement;
|
||||
this._currentDoorLockMode = parseInt(target.value) as DoorLockMode;
|
||||
}
|
||||
|
||||
private async _saveConfig(ev: CustomEvent) {
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import { LitElement, css, html, nothing } from "lit";
|
||||
import { LitElement, css, html } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import "../../../../../../components/buttons/ha-progress-button";
|
||||
import type { HaProgressButton } from "../../../../../../components/buttons/ha-progress-button";
|
||||
import "../../../../../../components/ha-alert";
|
||||
import "../../../../../../components/ha-formfield";
|
||||
import "../../../../../../components/ha-list-item";
|
||||
import "../../../../../../components/ha-select";
|
||||
import type { HaSelect } from "../../../../../../components/ha-select";
|
||||
import "../../../../../../components/ha-switch";
|
||||
import type { HaSwitch } from "../../../../../../components/ha-switch";
|
||||
import "../../../../../../components/ha-textfield";
|
||||
@@ -33,8 +35,6 @@ class ZWaveJSCapabilityMultiLevelSwitch extends LitElement {
|
||||
|
||||
@state() private _error?: string;
|
||||
|
||||
@state() private _direction = "up";
|
||||
|
||||
protected render() {
|
||||
return html`
|
||||
<h3>
|
||||
@@ -44,28 +44,23 @@ class ZWaveJSCapabilityMultiLevelSwitch extends LitElement {
|
||||
</h3>
|
||||
${this._error
|
||||
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
|
||||
: nothing}
|
||||
: ""}
|
||||
<ha-select
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.config.zwave_js.node_installer.capability_controls.multilevel_switch.direction"
|
||||
)}
|
||||
.value=${this._direction}
|
||||
.options=${[
|
||||
{
|
||||
value: "up",
|
||||
label: this.hass.localize(
|
||||
"ui.panel.config.zwave_js.node_installer.capability_controls.multilevel_switch.up"
|
||||
),
|
||||
},
|
||||
{
|
||||
value: "down",
|
||||
label: this.hass.localize(
|
||||
"ui.panel.config.zwave_js.node_installer.capability_controls.multilevel_switch.down"
|
||||
),
|
||||
},
|
||||
]}
|
||||
@selected=${this._directionChanged}
|
||||
id="direction"
|
||||
>
|
||||
<ha-list-item .value=${"up"} selected
|
||||
>${this.hass.localize(
|
||||
"ui.panel.config.zwave_js.node_installer.capability_controls.multilevel_switch.up"
|
||||
)}</ha-list-item
|
||||
>
|
||||
<ha-list-item .value=${"down"}
|
||||
>${this.hass.localize(
|
||||
"ui.panel.config.zwave_js.node_installer.capability_controls.multilevel_switch.down"
|
||||
)}</ha-list-item
|
||||
>
|
||||
</ha-select>
|
||||
<ha-formfield
|
||||
.label=${this.hass.localize(
|
||||
@@ -108,6 +103,9 @@ class ZWaveJSCapabilityMultiLevelSwitch extends LitElement {
|
||||
const button = ev.currentTarget as HaProgressButton;
|
||||
button.progress = true;
|
||||
|
||||
const direction = (this.shadowRoot!.getElementById("direction") as HaSelect)
|
||||
.value;
|
||||
|
||||
const ignoreStartLevel = (
|
||||
this.shadowRoot!.getElementById("ignore_start_level") as HaSwitch
|
||||
).checked;
|
||||
@@ -117,7 +115,7 @@ class ZWaveJSCapabilityMultiLevelSwitch extends LitElement {
|
||||
);
|
||||
|
||||
const options = {
|
||||
direction: this._direction,
|
||||
direction,
|
||||
ignoreStartLevel,
|
||||
startLevel,
|
||||
};
|
||||
@@ -148,10 +146,6 @@ class ZWaveJSCapabilityMultiLevelSwitch extends LitElement {
|
||||
button.progress = false;
|
||||
}
|
||||
|
||||
private _directionChanged(ev: CustomEvent<{ value: string }>) {
|
||||
this._direction = ev.detail.value;
|
||||
}
|
||||
|
||||
static styles = css`
|
||||
ha-select,
|
||||
ha-formfield,
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import { LitElement, css, html, nothing } from "lit";
|
||||
import { LitElement, css, html } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import "../../../../../../components/buttons/ha-progress-button";
|
||||
import type { HaProgressButton } from "../../../../../../components/buttons/ha-progress-button";
|
||||
import "../../../../../../components/ha-alert";
|
||||
import "../../../../../../components/ha-button";
|
||||
import "../../../../../../components/ha-list-item";
|
||||
import "../../../../../../components/ha-select";
|
||||
import type { HaSelect } from "../../../../../../components/ha-select";
|
||||
import "../../../../../../components/ha-textfield";
|
||||
import type { HaTextField } from "../../../../../../components/ha-textfield";
|
||||
import type { DeviceRegistryEntry } from "../../../../../../data/device/device_registry";
|
||||
@@ -35,12 +37,13 @@ class ZWaveJSCapabilityThermostatSetback extends LitElement {
|
||||
|
||||
@state() private _disableSetbackState = false;
|
||||
|
||||
@state() private _setbackType = "0";
|
||||
|
||||
@state() private _setbackSpecialType?: string;
|
||||
@query("#setback_type") private _setbackTypeInput!: HaSelect;
|
||||
|
||||
@query("#setback_state") private _setbackStateInput!: HaTextField;
|
||||
|
||||
@query("#setback_special_state")
|
||||
private _setbackSpecialStateSelect!: HaSelect;
|
||||
|
||||
@state() private _error?: string;
|
||||
|
||||
@state() private _loading = true;
|
||||
@@ -54,21 +57,23 @@ class ZWaveJSCapabilityThermostatSetback extends LitElement {
|
||||
</h3>
|
||||
${this._error
|
||||
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
|
||||
: nothing}
|
||||
: ""}
|
||||
<ha-select
|
||||
.label=${this.hass.localize(
|
||||
`ui.panel.config.zwave_js.node_installer.capability_controls.thermostat_setback.setback_type.label`
|
||||
)}
|
||||
@selected=${this._setbackTypeChanged}
|
||||
.value=${this._setbackType}
|
||||
id="setback_type"
|
||||
.value=${"0"}
|
||||
.disabled=${this._loading}
|
||||
.options=${SETBACK_TYPE_OPTIONS.map((translationKey, index) => ({
|
||||
value: String(index),
|
||||
label: this.hass.localize(
|
||||
`ui.panel.config.zwave_js.node_installer.capability_controls.thermostat_setback.setback_type.${translationKey}`
|
||||
),
|
||||
}))}
|
||||
>
|
||||
${SETBACK_TYPE_OPTIONS.map(
|
||||
(translationKey, index) =>
|
||||
html`<ha-list-item .value=${String(index)}>
|
||||
${this.hass.localize(
|
||||
`ui.panel.config.zwave_js.node_installer.capability_controls.thermostat_setback.setback_type.${translationKey}`
|
||||
)}
|
||||
</ha-list-item>`
|
||||
)}
|
||||
</ha-select>
|
||||
<div class="setback-state">
|
||||
<ha-textfield
|
||||
@@ -93,17 +98,16 @@ class ZWaveJSCapabilityThermostatSetback extends LitElement {
|
||||
id="setback_special_state"
|
||||
@change=${this._changeSpecialState}
|
||||
.disabled=${this._loading}
|
||||
.value=${this._setbackSpecialType}
|
||||
clearable
|
||||
.options=${Object.entries(SpecialState).map(
|
||||
([translationKey, value]) => ({
|
||||
value: value,
|
||||
label: this.hass.localize(
|
||||
`ui.panel.config.zwave_js.node_installer.capability_controls.thermostat_setback.setback_special_state.${translationKey}`
|
||||
),
|
||||
})
|
||||
)}
|
||||
>
|
||||
<ha-list-item selected> </ha-list-item>
|
||||
${Object.entries(SpecialState).map(
|
||||
([translationKey, value]) =>
|
||||
html`<ha-list-item .value=${value}>
|
||||
${this.hass.localize(
|
||||
`ui.panel.config.zwave_js.node_installer.capability_controls.thermostat_setback.setback_special_state.${translationKey}`
|
||||
)}
|
||||
</ha-list-item>`
|
||||
)}
|
||||
</ha-select>
|
||||
</div>
|
||||
<div class="actions">
|
||||
@@ -140,12 +144,12 @@ class ZWaveJSCapabilityThermostatSetback extends LitElement {
|
||||
true
|
||||
)) as { setbackType: number; setbackState: number | SpecialState };
|
||||
|
||||
this._setbackType = String(setbackType);
|
||||
this._setbackTypeInput.value = String(setbackType);
|
||||
if (typeof setbackState === "number") {
|
||||
this._setbackStateInput.value = String(setbackState);
|
||||
this._setbackSpecialType = undefined;
|
||||
this._setbackSpecialStateSelect.value = "";
|
||||
} else {
|
||||
this._setbackSpecialType = setbackState;
|
||||
this._setbackSpecialStateSelect.value = setbackState;
|
||||
}
|
||||
} catch (err) {
|
||||
this._error = this.hass.localize(
|
||||
@@ -157,9 +161,8 @@ class ZWaveJSCapabilityThermostatSetback extends LitElement {
|
||||
this._loading = false;
|
||||
}
|
||||
|
||||
private _changeSpecialState(ev: CustomEvent<{ value: string }>) {
|
||||
this._disableSetbackState = !ev.detail.value;
|
||||
this._setbackSpecialType = ev.detail.value;
|
||||
private _changeSpecialState() {
|
||||
this._disableSetbackState = !!this._setbackSpecialStateSelect.value;
|
||||
}
|
||||
|
||||
private async _saveSetback(ev: CustomEvent) {
|
||||
@@ -167,11 +170,11 @@ class ZWaveJSCapabilityThermostatSetback extends LitElement {
|
||||
button.progress = true;
|
||||
|
||||
this._error = undefined;
|
||||
const setbackType = this._setbackType;
|
||||
const setbackType = this._setbackTypeInput.value;
|
||||
|
||||
let setbackState: number | string = Number(this._setbackStateInput.value);
|
||||
if (this._setbackSpecialType) {
|
||||
setbackState = this._setbackSpecialType;
|
||||
if (this._setbackSpecialStateSelect.value) {
|
||||
setbackState = this._setbackSpecialStateSelect.value;
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -201,10 +204,6 @@ class ZWaveJSCapabilityThermostatSetback extends LitElement {
|
||||
this._loadSetback();
|
||||
}
|
||||
|
||||
private _setbackTypeChanged(ev: CustomEvent<{ value: string }>) {
|
||||
this._setbackType = ev.detail.value;
|
||||
}
|
||||
|
||||
static styles = css`
|
||||
:host {
|
||||
display: flex;
|
||||
|
||||
@@ -151,6 +151,7 @@ class ZWaveJSConfigDashboard extends SubscribeMixin(LitElement) {
|
||||
.narrow=${this.narrow}
|
||||
.route=${this.route}
|
||||
.tabs=${configTabs}
|
||||
back-path="/config"
|
||||
has-fab
|
||||
>
|
||||
<ha-icon-button
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
import { mdiCloseCircle } from "@mdi/js";
|
||||
import { LitElement, css, html, nothing } from "lit";
|
||||
import { LitElement, html, css, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { fireEvent } from "../../../../../common/dom/fire_event";
|
||||
import "../../../../../components/ha-button";
|
||||
import "../../../../../components/ha-select";
|
||||
import "../../../../../components/ha-spinner";
|
||||
import { mdiCloseCircle } from "@mdi/js";
|
||||
import "../../../../../components/ha-textfield";
|
||||
import "../../../../../components/ha-select";
|
||||
import "../../../../../components/ha-button";
|
||||
import "../../../../../components/ha-spinner";
|
||||
import "../../../../../components/ha-list-item";
|
||||
import type { HomeAssistant } from "../../../../../types";
|
||||
import {
|
||||
getZwaveNodeRawConfigParameter,
|
||||
setZwaveNodeRawConfigParameter,
|
||||
} from "../../../../../data/zwave_js";
|
||||
import type { HomeAssistant } from "../../../../../types";
|
||||
import { fireEvent } from "../../../../../common/dom/fire_event";
|
||||
|
||||
@customElement("zwave_js-custom-param")
|
||||
class ZWaveJSCustomParam extends LitElement {
|
||||
@@ -47,8 +48,10 @@ class ZWaveJSCustomParam extends LitElement {
|
||||
)}
|
||||
.value=${String(this._valueSize)}
|
||||
@selected=${this._customValueSizeChanged}
|
||||
.options=${["1", "2", "4"]}
|
||||
>
|
||||
<ha-list-item value="1">1</ha-list-item>
|
||||
<ha-list-item value="2">2</ha-list-item>
|
||||
<ha-list-item value="4">4</ha-list-item>
|
||||
</ha-select>
|
||||
<ha-textfield
|
||||
.label=${this.hass.localize(
|
||||
@@ -64,33 +67,27 @@ class ZWaveJSCustomParam extends LitElement {
|
||||
)}
|
||||
.value=${String(this._valueFormat)}
|
||||
@selected=${this._customValueFormatChanged}
|
||||
.options=${[
|
||||
{
|
||||
value: "0",
|
||||
label: this.hass.localize(
|
||||
"ui.panel.config.zwave_js.node_config.signed"
|
||||
),
|
||||
},
|
||||
{
|
||||
value: "1",
|
||||
label: this.hass.localize(
|
||||
"ui.panel.config.zwave_js.node_config.unsigned"
|
||||
),
|
||||
},
|
||||
{
|
||||
value: "2",
|
||||
label: this.hass.localize(
|
||||
"ui.panel.config.zwave_js.node_config.enumerated"
|
||||
),
|
||||
},
|
||||
{
|
||||
value: "3",
|
||||
label: this.hass.localize(
|
||||
"ui.panel.config.zwave_js.node_config.bitfield"
|
||||
),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<ha-list-item value="0"
|
||||
>${this.hass.localize(
|
||||
"ui.panel.config.zwave_js.node_config.signed"
|
||||
)}</ha-list-item
|
||||
>
|
||||
<ha-list-item value="1"
|
||||
>${this.hass.localize(
|
||||
"ui.panel.config.zwave_js.node_config.unsigned"
|
||||
)}</ha-list-item
|
||||
>
|
||||
<ha-list-item value="2"
|
||||
>${this.hass.localize(
|
||||
"ui.panel.config.zwave_js.node_config.enumerated"
|
||||
)}</ha-list-item
|
||||
>
|
||||
<ha-list-item value="3"
|
||||
>${this.hass.localize(
|
||||
"ui.panel.config.zwave_js.node_config.bitfield"
|
||||
)}</ha-list-item
|
||||
>
|
||||
</ha-select>
|
||||
</div>
|
||||
<div class="custom-config-buttons">
|
||||
@@ -132,16 +129,18 @@ class ZWaveJSCustomParam extends LitElement {
|
||||
);
|
||||
}
|
||||
|
||||
private _customValueSizeChanged(ev: CustomEvent<{ value: string }>) {
|
||||
this._valueSize = this._tryParseNumber(ev.detail.value) ?? 1;
|
||||
private _customValueSizeChanged(ev: Event) {
|
||||
this._valueSize =
|
||||
this._tryParseNumber((ev.target as HTMLSelectElement).value) ?? 1;
|
||||
}
|
||||
|
||||
private _customValueChanged(ev: Event) {
|
||||
this._value = this._tryParseNumber((ev.target as HTMLInputElement).value);
|
||||
}
|
||||
|
||||
private _customValueFormatChanged(ev: CustomEvent<{ value: string }>) {
|
||||
this._valueFormat = this._tryParseNumber(ev.detail.value) ?? 0;
|
||||
private _customValueFormatChanged(ev: Event) {
|
||||
this._valueFormat =
|
||||
this._tryParseNumber((ev.target as HTMLSelectElement).value) ?? 0;
|
||||
}
|
||||
|
||||
private async _getCustomConfigValue() {
|
||||
|
||||
@@ -5,6 +5,7 @@ import { css, html, LitElement } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { capitalizeFirstLetter } from "../../../../../common/string/capitalize-first-letter";
|
||||
import "../../../../../components/ha-icon-button";
|
||||
import "../../../../../components/ha-list-item";
|
||||
import "../../../../../components/ha-select";
|
||||
import type { ZWaveJSLogConfig } from "../../../../../data/zwave_js";
|
||||
import {
|
||||
@@ -66,6 +67,7 @@ class ZWaveJSLogs extends SubscribeMixin(LitElement) {
|
||||
.narrow=${this.narrow}
|
||||
.route=${this.route}
|
||||
.tabs=${configTabs}
|
||||
back-path="/config"
|
||||
>
|
||||
<div class="container">
|
||||
<ha-card>
|
||||
@@ -83,18 +85,13 @@ class ZWaveJSLogs extends SubscribeMixin(LitElement) {
|
||||
)}
|
||||
.value=${this._logConfig.level}
|
||||
@selected=${this._dropdownSelected}
|
||||
.options=${[
|
||||
"error",
|
||||
"warn",
|
||||
"info",
|
||||
"verbose",
|
||||
"debug",
|
||||
"silly",
|
||||
].map((level) => ({
|
||||
value: level,
|
||||
label: capitalizeFirstLetter(level),
|
||||
}))}
|
||||
>
|
||||
<ha-list-item value="error">Error</ha-list-item>
|
||||
<ha-list-item value="warn">Warn</ha-list-item>
|
||||
<ha-list-item value="info">Info</ha-list-item>
|
||||
<ha-list-item value="verbose">Verbose</ha-list-item>
|
||||
<ha-list-item value="debug">Debug</ha-list-item>
|
||||
<ha-list-item value="silly">Silly</ha-list-item>
|
||||
</ha-select>
|
||||
`
|
||||
: ""}
|
||||
@@ -137,11 +134,11 @@ class ZWaveJSLogs extends SubscribeMixin(LitElement) {
|
||||
);
|
||||
}
|
||||
|
||||
private _dropdownSelected(ev: CustomEvent<{ value: string }>) {
|
||||
private _dropdownSelected(ev) {
|
||||
if (ev.target === undefined || this._logConfig === undefined) {
|
||||
return;
|
||||
}
|
||||
const selected = ev.detail.value;
|
||||
const selected = ev.target.value;
|
||||
if (this._logConfig.level === selected) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -77,6 +77,7 @@ export class ZWaveJSNetworkVisualization extends SubscribeMixin(LitElement) {
|
||||
.narrow=${this.narrow}
|
||||
.route=${this.route}
|
||||
.tabs=${configTabs}
|
||||
back-path="/config"
|
||||
>
|
||||
<ha-network-graph
|
||||
.hass=${this.hass}
|
||||
|
||||
@@ -17,6 +17,7 @@ import type { HaProgressButton } from "../../../../../components/buttons/ha-prog
|
||||
import "../../../../../components/ha-alert";
|
||||
import "../../../../../components/ha-card";
|
||||
import "../../../../../components/ha-generic-picker";
|
||||
import "../../../../../components/ha-list-item";
|
||||
import type { PickerComboBoxItem } from "../../../../../components/ha-picker-combo-box";
|
||||
import "../../../../../components/ha-select";
|
||||
import "../../../../../components/ha-selector/ha-selector-boolean";
|
||||
@@ -373,6 +374,7 @@ class ZWaveJSNodeConfig extends LitElement {
|
||||
return html`
|
||||
${labelAndDescription}
|
||||
<ha-select
|
||||
fixedMenuPosition
|
||||
.disabled=${!item.metadata.writeable}
|
||||
.value=${item.value?.toString()}
|
||||
.key=${id}
|
||||
@@ -381,13 +383,12 @@ class ZWaveJSNodeConfig extends LitElement {
|
||||
.propertyKey=${item.property_key}
|
||||
@selected=${this._dropdownSelected}
|
||||
.helper=${defaultLabel}
|
||||
.options=${Object.entries(item.metadata.states).map(
|
||||
([key, entityState]) => ({
|
||||
value: key,
|
||||
label: entityState,
|
||||
})
|
||||
)}
|
||||
>
|
||||
${Object.entries(item.metadata.states).map(
|
||||
([key, entityState]) => html`
|
||||
<ha-list-item .value=${key}>${entityState}</ha-list-item>
|
||||
`
|
||||
)}
|
||||
</ha-select>
|
||||
`;
|
||||
}
|
||||
@@ -456,8 +457,8 @@ class ZWaveJSNodeConfig extends LitElement {
|
||||
this._updateConfigParameter(ev.target, ev.detail.value ? 1 : 0);
|
||||
}
|
||||
|
||||
private _dropdownSelected(ev: CustomEvent<{ value: string }>) {
|
||||
this._handleEnumeratedPickerValueChanged(ev, ev.detail.value);
|
||||
private _dropdownSelected(ev) {
|
||||
this._handleEnumeratedPickerValueChanged(ev, ev.target.value);
|
||||
}
|
||||
|
||||
private _pickerValueChanged(ev) {
|
||||
@@ -468,7 +469,7 @@ class ZWaveJSNodeConfig extends LitElement {
|
||||
if (ev.target === undefined || this._config![ev.target.key] === undefined) {
|
||||
return;
|
||||
}
|
||||
if (this._config![ev.target.key].value === value) {
|
||||
if (this._config![ev.target.key].value?.toString() === value) {
|
||||
return;
|
||||
}
|
||||
this._setResult(ev.target.key, undefined);
|
||||
|
||||
@@ -12,6 +12,7 @@ import type { PropertyValues } from "lit";
|
||||
import { LitElement, html, nothing } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { formatShortDateTime } from "../../../common/datetime/format_date_time";
|
||||
import { storage } from "../../../common/decorators/storage";
|
||||
import { navigate } from "../../../common/navigate";
|
||||
import type { LocalizeFunc } from "../../../common/translations/localize";
|
||||
@@ -46,10 +47,6 @@ import "../../../layouts/hass-tabs-subpage-data-table";
|
||||
import type { HomeAssistant, Route } from "../../../types";
|
||||
import { configSections } from "../ha-panel-config";
|
||||
import { showLabelDetailDialog } from "./show-dialog-label-detail";
|
||||
import {
|
||||
getCreatedAtTableColumn,
|
||||
getModifiedAtTableColumn,
|
||||
} from "../common/data-table-columns";
|
||||
|
||||
@customElement("ha-config-labels")
|
||||
export class HaConfigLabels extends LitElement {
|
||||
@@ -138,8 +135,34 @@ export class HaConfigLabels extends LitElement {
|
||||
filterable: true,
|
||||
hideable: true,
|
||||
},
|
||||
created_at: getCreatedAtTableColumn(localize, this.hass),
|
||||
modified_at: getModifiedAtTableColumn(localize, this.hass),
|
||||
created_at: {
|
||||
title: localize("ui.panel.config.generic.headers.created_at"),
|
||||
defaultHidden: true,
|
||||
sortable: true,
|
||||
minWidth: "128px",
|
||||
template: (label) =>
|
||||
label.created_at
|
||||
? formatShortDateTime(
|
||||
new Date(label.created_at * 1000),
|
||||
this.hass.locale,
|
||||
this.hass.config
|
||||
)
|
||||
: "—",
|
||||
},
|
||||
modified_at: {
|
||||
title: localize("ui.panel.config.generic.headers.modified_at"),
|
||||
defaultHidden: true,
|
||||
sortable: true,
|
||||
minWidth: "128px",
|
||||
template: (label) =>
|
||||
label.modified_at
|
||||
? formatShortDateTime(
|
||||
new Date(label.modified_at * 1000),
|
||||
this.hass.locale,
|
||||
this.hass.config
|
||||
)
|
||||
: "—",
|
||||
},
|
||||
actions: {
|
||||
title: "",
|
||||
label: localize("ui.panel.config.generic.headers.actions"),
|
||||
|
||||
@@ -758,10 +758,6 @@ class ErrorLogCard extends LitElement {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.card-content {
|
||||
margin-top: -8px;
|
||||
}
|
||||
|
||||
.error-log {
|
||||
position: relative;
|
||||
font-family: var(--ha-font-family-code);
|
||||
@@ -769,7 +765,7 @@ class ErrorLogCard extends LitElement {
|
||||
text-align: start;
|
||||
padding-top: 16px;
|
||||
padding-bottom: 16px;
|
||||
overflow: auto;
|
||||
overflow-y: scroll;
|
||||
min-height: var(--error-log-card-height, calc(100vh - 244px));
|
||||
max-height: var(--error-log-card-height, calc(100vh - 244px));
|
||||
border-top: 1px solid var(--divider-color);
|
||||
|
||||
@@ -80,7 +80,7 @@ export class DialogLovelaceDashboardDetail extends LitElement {
|
||||
)}
|
||||
>
|
||||
<div>
|
||||
${this._params.dashboard?.mode === "yaml"
|
||||
${this._params.dashboard && !this._params.dashboard.id
|
||||
? this.hass.localize(
|
||||
"ui.panel.config.lovelace.dashboards.cant_edit_yaml"
|
||||
)
|
||||
@@ -97,7 +97,7 @@ export class DialogLovelaceDashboardDetail extends LitElement {
|
||||
</div>
|
||||
${this._params.urlPath
|
||||
? html`
|
||||
${this._params.dashboard?.mode === "storage"
|
||||
${this._params.dashboard?.id
|
||||
? html`
|
||||
<ha-button
|
||||
slot="secondaryAction"
|
||||
@@ -123,7 +123,7 @@ export class DialogLovelaceDashboardDetail extends LitElement {
|
||||
dialogInitialFocus
|
||||
>
|
||||
${this._params.urlPath
|
||||
? this._params.dashboard?.mode === "storage"
|
||||
? this._params.dashboard?.id
|
||||
? this.hass.localize(
|
||||
"ui.panel.config.lovelace.dashboards.detail.update"
|
||||
)
|
||||
@@ -232,9 +232,8 @@ export class DialogLovelaceDashboardDetail extends LitElement {
|
||||
}
|
||||
|
||||
private async _updateDashboard() {
|
||||
if (this._params?.urlPath && this._params.dashboard?.mode === "yaml") {
|
||||
if (this._params?.urlPath && !this._params.dashboard?.id) {
|
||||
this.closeDialog();
|
||||
return;
|
||||
}
|
||||
this._submitting = true;
|
||||
try {
|
||||
|
||||
@@ -281,8 +281,7 @@ export class HaConfigLovelaceDashboards extends LitElement {
|
||||
action: () => this._handleSetAsDefault(dashboard),
|
||||
disabled: dashboard.default,
|
||||
},
|
||||
...(dashboard.type === "user_created" &&
|
||||
dashboard.mode === "storage"
|
||||
...(dashboard.type === "user_created"
|
||||
? [
|
||||
{
|
||||
path: mdiPencil,
|
||||
|
||||
@@ -12,22 +12,27 @@ import {
|
||||
mdiOpenInNew,
|
||||
mdiPalette,
|
||||
mdiPencil,
|
||||
mdiPencilOff,
|
||||
mdiPlay,
|
||||
mdiPlus,
|
||||
mdiTag,
|
||||
mdiTextureBox,
|
||||
} from "@mdi/js";
|
||||
import { differenceInDays } from "date-fns";
|
||||
import type { UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||
import type { CSSResultGroup, PropertyValues, TemplateResult } from "lit";
|
||||
import { LitElement, css, html, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { computeCssColor } from "../../../common/color/compute-color";
|
||||
import { formatShortDateTimeWithConditionalYear } from "../../../common/datetime/format_date_time";
|
||||
import { relativeTime } from "../../../common/datetime/relative_time";
|
||||
import { storage } from "../../../common/decorators/storage";
|
||||
import type { HASSDomEvent } from "../../../common/dom/fire_event";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import { computeStateName } from "../../../common/entity/compute_state_name";
|
||||
import { navigate } from "../../../common/navigate";
|
||||
import { slugify } from "../../../common/string/slugify";
|
||||
import type { LocalizeFunc } from "../../../common/translations/localize";
|
||||
import {
|
||||
hasRejectedItems,
|
||||
@@ -74,6 +79,7 @@ import {
|
||||
isRelatedItemsFilterUsed,
|
||||
serializeFilters,
|
||||
} from "../../../data/data_table_filters";
|
||||
import { isUnavailableState } from "../../../data/entity/entity";
|
||||
import type {
|
||||
EntityRegistryEntry,
|
||||
UpdateEntityRegistryEntryResult,
|
||||
@@ -110,13 +116,6 @@ import { showAssignCategoryDialog } from "../category/show-dialog-assign-categor
|
||||
import { showCategoryRegistryDetailDialog } from "../category/show-dialog-category-registry-detail";
|
||||
import { configSections } from "../ha-panel-config";
|
||||
import { showLabelDetailDialog } from "../labels/show-dialog-label-detail";
|
||||
import {
|
||||
getAreaTableColumn,
|
||||
getCategoryTableColumn,
|
||||
getLabelsTableColumn,
|
||||
getEditableTableColumn,
|
||||
renderRelativeTimeColumn,
|
||||
} from "../common/data-table-columns";
|
||||
import {
|
||||
getAssistantsSortableKey,
|
||||
getAssistantsTableColumn,
|
||||
@@ -129,10 +128,9 @@ type SceneItem = SceneEntity & {
|
||||
name: string;
|
||||
area: string | undefined;
|
||||
category: string | undefined;
|
||||
label_entries: LabelRegistryEntry[];
|
||||
labels: LabelRegistryEntry[];
|
||||
assistants: string[];
|
||||
assistants_sortable_key: string | undefined;
|
||||
editable: boolean;
|
||||
};
|
||||
|
||||
@customElement("ha-scene-dashboard")
|
||||
@@ -259,13 +257,12 @@ class HaSceneDashboard extends SubscribeMixin(LitElement) {
|
||||
category: category
|
||||
? categoryReg?.find((cat) => cat.category_id === category)?.name
|
||||
: undefined,
|
||||
label_entries: (labels || []).map(
|
||||
labels: (labels || []).map(
|
||||
(lbl) => labelReg!.find((label) => label.label_id === lbl)!
|
||||
),
|
||||
assistants,
|
||||
assistants_sortable_key: getAssistantsSortableKey(assistants),
|
||||
selectable: entityRegEntry !== undefined,
|
||||
editable: Boolean(scene.attributes.id),
|
||||
};
|
||||
});
|
||||
}
|
||||
@@ -298,34 +295,89 @@ class HaSceneDashboard extends SubscribeMixin(LitElement) {
|
||||
direction: "asc",
|
||||
flex: 2,
|
||||
extraTemplate: (scene) =>
|
||||
scene.label_entries.length
|
||||
scene.labels.length
|
||||
? html`<ha-data-table-labels
|
||||
@label-clicked=${this._labelClicked}
|
||||
.labels=${scene.label_entries}
|
||||
.labels=${scene.labels}
|
||||
></ha-data-table-labels>`
|
||||
: nothing,
|
||||
},
|
||||
area: getAreaTableColumn(localize),
|
||||
category: getCategoryTableColumn(localize),
|
||||
labels: getLabelsTableColumn(),
|
||||
area: {
|
||||
title: localize("ui.panel.config.scene.picker.headers.area"),
|
||||
groupable: true,
|
||||
filterable: true,
|
||||
sortable: true,
|
||||
},
|
||||
category: {
|
||||
title: localize("ui.panel.config.scene.picker.headers.category"),
|
||||
defaultHidden: true,
|
||||
groupable: true,
|
||||
filterable: true,
|
||||
sortable: true,
|
||||
},
|
||||
labels: {
|
||||
title: "",
|
||||
hidden: true,
|
||||
filterable: true,
|
||||
template: (scene) => scene.labels.map((lbl) => lbl.name).join(" "),
|
||||
},
|
||||
state: {
|
||||
title: localize(
|
||||
"ui.panel.config.scene.picker.headers.last_activated"
|
||||
),
|
||||
sortable: true,
|
||||
template: (scene) =>
|
||||
renderRelativeTimeColumn(
|
||||
scene.state,
|
||||
"last-activated",
|
||||
scene.entity_id,
|
||||
localize,
|
||||
this.hass
|
||||
),
|
||||
template: (scene) => {
|
||||
const lastActivated = scene.state;
|
||||
if (!lastActivated || isUnavailableState(lastActivated)) {
|
||||
return localize("ui.components.relative_time.never");
|
||||
}
|
||||
const date = new Date(scene.state);
|
||||
const now = new Date();
|
||||
const dayDifference = differenceInDays(now, date);
|
||||
const formattedTime = formatShortDateTimeWithConditionalYear(
|
||||
date,
|
||||
this.hass.locale,
|
||||
this.hass.config
|
||||
);
|
||||
const elementId = "last-activated-" + slugify(scene.entity_id);
|
||||
return html`
|
||||
${dayDifference > 3
|
||||
? formattedTime
|
||||
: html`
|
||||
<ha-tooltip for=${elementId}>${formattedTime}</ha-tooltip>
|
||||
<span id=${elementId}
|
||||
>${relativeTime(date, this.hass.locale)}</span
|
||||
>
|
||||
`}
|
||||
`;
|
||||
},
|
||||
},
|
||||
only_editable: {
|
||||
title: "",
|
||||
label: this.hass.localize(
|
||||
"ui.panel.config.scene.picker.headers.editable"
|
||||
),
|
||||
type: "icon",
|
||||
showNarrow: true,
|
||||
template: (scene) =>
|
||||
!scene.attributes.id
|
||||
? html`
|
||||
<ha-svg-icon
|
||||
.id="svg-icon-${slugify(scene.entity_id)}"
|
||||
.path=${mdiPencilOff}
|
||||
style="color: var(--secondary-text-color)"
|
||||
></ha-svg-icon>
|
||||
<ha-tooltip
|
||||
.for="svg-icon-${slugify(scene.entity_id)}"
|
||||
placement="left"
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.scene.picker.only_editable"
|
||||
)}
|
||||
</ha-tooltip>
|
||||
`
|
||||
: nothing,
|
||||
},
|
||||
only_editable: getEditableTableColumn(
|
||||
localize,
|
||||
localize("ui.panel.config.scene.picker.only_editable")
|
||||
),
|
||||
actions: {
|
||||
title: "",
|
||||
label: this.hass.localize("ui.panel.config.generic.headers.actions"),
|
||||
@@ -372,7 +424,7 @@ class HaSceneDashboard extends SubscribeMixin(LitElement) {
|
||||
"ui.panel.config.scene.editor.rename"
|
||||
),
|
||||
action: () => this._rename(scene),
|
||||
disabled: !scene.editable,
|
||||
disabled: !scene.attributes.id,
|
||||
},
|
||||
{
|
||||
divider: true,
|
||||
@@ -383,7 +435,7 @@ class HaSceneDashboard extends SubscribeMixin(LitElement) {
|
||||
"ui.panel.config.scene.picker.duplicate"
|
||||
),
|
||||
action: () => this._duplicate(scene),
|
||||
disabled: !scene.editable,
|
||||
disabled: !scene.attributes.id,
|
||||
},
|
||||
{
|
||||
label: this.hass.localize(
|
||||
@@ -391,8 +443,8 @@ class HaSceneDashboard extends SubscribeMixin(LitElement) {
|
||||
),
|
||||
path: mdiDelete,
|
||||
action: () => this._deleteConfirm(scene),
|
||||
warning: scene.editable,
|
||||
disabled: !scene.editable,
|
||||
warning: scene.attributes.id,
|
||||
disabled: !scene.attributes.id,
|
||||
},
|
||||
]}
|
||||
>
|
||||
@@ -1014,7 +1066,7 @@ ${rejected
|
||||
}
|
||||
}
|
||||
|
||||
private async _duplicate(scene: SceneEntity) {
|
||||
private async _duplicate(scene) {
|
||||
if (scene.attributes.id) {
|
||||
const config = await getSceneConfig(this.hass, scene.attributes.id);
|
||||
const entityRegEntry = this._entityReg.find(
|
||||
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
mdiTextureBox,
|
||||
mdiTransitConnection,
|
||||
} from "@mdi/js";
|
||||
import { differenceInDays } from "date-fns";
|
||||
import type { UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||
import type { CSSResultGroup, PropertyValues, TemplateResult } from "lit";
|
||||
import { LitElement, css, html, nothing } from "lit";
|
||||
@@ -25,11 +26,14 @@ import { styleMap } from "lit/directives/style-map";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { computeCssColor } from "../../../common/color/compute-color";
|
||||
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
|
||||
import { formatShortDateTimeWithConditionalYear } from "../../../common/datetime/format_date_time";
|
||||
import { relativeTime } from "../../../common/datetime/relative_time";
|
||||
import { storage } from "../../../common/decorators/storage";
|
||||
import type { HASSDomEvent } from "../../../common/dom/fire_event";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import { computeStateName } from "../../../common/entity/compute_state_name";
|
||||
import { navigate } from "../../../common/navigate";
|
||||
import { slugify } from "../../../common/string/slugify";
|
||||
import type { LocalizeFunc } from "../../../common/translations/localize";
|
||||
import {
|
||||
hasRejectedItems,
|
||||
@@ -112,13 +116,6 @@ import { showAreaRegistryDetailDialog } from "../areas/show-dialog-area-registry
|
||||
import { showNewAutomationDialog } from "../automation/show-dialog-new-automation";
|
||||
import { showAssignCategoryDialog } from "../category/show-dialog-assign-category";
|
||||
import { showCategoryRegistryDetailDialog } from "../category/show-dialog-category-registry-detail";
|
||||
import {
|
||||
getEntityIdHiddenTableColumn,
|
||||
getAreaTableColumn,
|
||||
getCategoryTableColumn,
|
||||
getLabelsTableColumn,
|
||||
getTriggeredAtTableColumn,
|
||||
} from "../common/data-table-columns";
|
||||
import { configSections } from "../ha-panel-config";
|
||||
import { showLabelDetailDialog } from "../labels/show-dialog-label-detail";
|
||||
import {
|
||||
@@ -133,7 +130,7 @@ type ScriptItem = ScriptEntity & {
|
||||
area: string | undefined;
|
||||
last_triggered: string | undefined;
|
||||
category: string | undefined;
|
||||
label_entries: LabelRegistryEntry[];
|
||||
labels: LabelRegistryEntry[];
|
||||
assistants: string[];
|
||||
assistants_sortable_key: string | undefined;
|
||||
};
|
||||
@@ -267,7 +264,7 @@ class HaScriptPicker extends SubscribeMixin(LitElement) {
|
||||
category: category
|
||||
? categoryReg?.find((cat) => cat.category_id === category)?.name
|
||||
: undefined,
|
||||
label_entries: (labels || []).map(
|
||||
labels: (labels || []).map(
|
||||
(lbl) => labelReg!.find((label) => label.label_id === lbl)!
|
||||
),
|
||||
assistants,
|
||||
@@ -300,7 +297,6 @@ class HaScriptPicker extends SubscribeMixin(LitElement) {
|
||||
})}
|
||||
></ha-state-icon>`,
|
||||
},
|
||||
entity_id: getEntityIdHiddenTableColumn(),
|
||||
name: {
|
||||
title: localize("ui.panel.config.script.picker.headers.name"),
|
||||
main: true,
|
||||
@@ -309,17 +305,60 @@ class HaScriptPicker extends SubscribeMixin(LitElement) {
|
||||
direction: "asc",
|
||||
flex: 2,
|
||||
extraTemplate: (script) =>
|
||||
script.label_entries.length
|
||||
script.labels.length
|
||||
? html`<ha-data-table-labels
|
||||
@label-clicked=${this._labelClicked}
|
||||
.labels=${script.label_entries}
|
||||
.labels=${script.labels}
|
||||
></ha-data-table-labels>`
|
||||
: nothing,
|
||||
},
|
||||
area: getAreaTableColumn(localize),
|
||||
category: getCategoryTableColumn(localize),
|
||||
labels: getLabelsTableColumn(),
|
||||
last_triggered: getTriggeredAtTableColumn(localize, this.hass),
|
||||
area: {
|
||||
title: localize("ui.panel.config.script.picker.headers.area"),
|
||||
groupable: true,
|
||||
filterable: true,
|
||||
sortable: true,
|
||||
},
|
||||
category: {
|
||||
title: localize("ui.panel.config.script.picker.headers.category"),
|
||||
defaultHidden: true,
|
||||
groupable: true,
|
||||
filterable: true,
|
||||
sortable: true,
|
||||
},
|
||||
labels: {
|
||||
title: "",
|
||||
hidden: true,
|
||||
filterable: true,
|
||||
template: (script) => script.labels.map((lbl) => lbl.name).join(" "),
|
||||
},
|
||||
last_triggered: {
|
||||
sortable: true,
|
||||
title: localize("ui.card.automation.last_triggered"),
|
||||
template: (script) => {
|
||||
if (!script.last_triggered) {
|
||||
return this.hass.localize("ui.components.relative_time.never");
|
||||
}
|
||||
const date = new Date(script.last_triggered);
|
||||
const now = new Date();
|
||||
const dayDifference = differenceInDays(now, date);
|
||||
const formattedTime = formatShortDateTimeWithConditionalYear(
|
||||
date,
|
||||
this.hass.locale,
|
||||
this.hass.config
|
||||
);
|
||||
const elementId = "last-triggered-" + slugify(script.entity_id);
|
||||
return html`
|
||||
${dayDifference > 3
|
||||
? formattedTime
|
||||
: html`
|
||||
<ha-tooltip for=${elementId}>${formattedTime}</ha-tooltip>
|
||||
<span id=${elementId}
|
||||
>${relativeTime(date, this.hass.locale)}</span
|
||||
>
|
||||
`}
|
||||
`;
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
title: "",
|
||||
label: this.hass.localize("ui.panel.config.generic.headers.actions"),
|
||||
|
||||
@@ -3,8 +3,10 @@ import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import "../../../components/ha-button";
|
||||
import { stopPropagation } from "../../../common/dom/stop_propagation";
|
||||
import "../../../components/ha-dialog";
|
||||
import "../../../components/ha-button";
|
||||
import "../../../components/ha-list-item";
|
||||
import "../../../components/ha-select";
|
||||
import "../../../components/ha-spinner";
|
||||
import {
|
||||
@@ -130,18 +132,26 @@ class MoveDatadiskDialog extends LitElement {
|
||||
"ui.panel.config.storage.datadisk.select_device"
|
||||
)}
|
||||
@selected=${this._selectDevice}
|
||||
.options=${this._disks.map((disk) => ({
|
||||
value: disk.id,
|
||||
label: `${disk.vendor} ${disk.model}`,
|
||||
secondary: this.hass.localize(
|
||||
"ui.panel.config.storage.datadisk.extra_information",
|
||||
{
|
||||
size: bytesToString(disk.size),
|
||||
serial: disk.serial,
|
||||
}
|
||||
),
|
||||
}))}
|
||||
></ha-select>
|
||||
@closed=${stopPropagation}
|
||||
dialogInitialFocus
|
||||
fixedMenuPosition
|
||||
>
|
||||
${this._disks.map(
|
||||
(disk) =>
|
||||
html`<ha-list-item twoline .value=${disk.id}>
|
||||
<span>${disk.vendor} ${disk.model}</span>
|
||||
<span slot="secondary">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.storage.datadisk.extra_information",
|
||||
{
|
||||
size: bytesToString(disk.size),
|
||||
serial: disk.serial,
|
||||
}
|
||||
)}
|
||||
</span>
|
||||
</ha-list-item>`
|
||||
)}
|
||||
</ha-select>
|
||||
|
||||
<ha-button
|
||||
slot="primaryAction"
|
||||
@@ -164,8 +174,8 @@ class MoveDatadiskDialog extends LitElement {
|
||||
`;
|
||||
}
|
||||
|
||||
private _selectDevice(ev: CustomEvent<{ value: string }>): void {
|
||||
this._selectedDevice = ev.detail.value;
|
||||
private _selectDevice(ev) {
|
||||
this._selectedDevice = ev.target.value;
|
||||
}
|
||||
|
||||
private async _moveDatadisk() {
|
||||
|
||||
@@ -242,7 +242,7 @@ export class StorageBreakdownChart extends LitElement {
|
||||
}
|
||||
|
||||
.chart-container {
|
||||
transition: height var(--ha-animation-duration-slow) ease;
|
||||
transition: height var(--ha-animation-base-duration) ease;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
@@ -264,7 +264,7 @@ export class StorageBreakdownChart extends LitElement {
|
||||
|
||||
ha-segmented-bar,
|
||||
ha-sunburst-chart {
|
||||
animation: fade-in var(--ha-animation-duration-slow) ease;
|
||||
animation: fade-in var(--ha-animation-base-duration) ease;
|
||||
}
|
||||
|
||||
@keyframes fade-in {
|
||||
|
||||
@@ -88,12 +88,6 @@ export class HaConfigTags extends SubscribeMixin(LitElement) {
|
||||
filterable: true,
|
||||
flex: 2,
|
||||
},
|
||||
id: {
|
||||
title: localize("ui.panel.config.tag.headers.tag_id"),
|
||||
main: true,
|
||||
sortable: true,
|
||||
filterable: true,
|
||||
},
|
||||
last_scanned_datetime: {
|
||||
title: localize("ui.panel.config.tag.headers.last_scanned"),
|
||||
sortable: true,
|
||||
|
||||
@@ -16,7 +16,9 @@ export function getAssistantsTableColumn<T>(
|
||||
visible?: boolean
|
||||
): DataTableColumnData<T> {
|
||||
return {
|
||||
title: localize("ui.panel.config.generic.headers.assistants"),
|
||||
title: localize(
|
||||
"ui.panel.config.voice_assistants.expose.headers.assistants"
|
||||
),
|
||||
type: "flex",
|
||||
defaultHidden: !visible,
|
||||
sortable: true,
|
||||
|
||||
@@ -49,11 +49,6 @@ import "../../../layouts/hass-tabs-subpage-data-table";
|
||||
import type { HaTabsSubpageDataTable } from "../../../layouts/hass-tabs-subpage-data-table";
|
||||
import { haStyle } from "../../../resources/styles";
|
||||
import type { HomeAssistant, Route } from "../../../types";
|
||||
import {
|
||||
getEntityIdTableColumn,
|
||||
getDomainTableColumn,
|
||||
getAreaTableColumn,
|
||||
} from "../common/data-table-columns";
|
||||
import "./expose/expose-assistant-icon";
|
||||
import {
|
||||
getAssistantsTableColumn,
|
||||
@@ -183,9 +178,30 @@ export class VoiceAssistantsExpose extends LitElement {
|
||||
direction: "asc",
|
||||
flex: 2,
|
||||
},
|
||||
area: getAreaTableColumn(localize),
|
||||
entity_id: getEntityIdTableColumn(localize, true),
|
||||
domain: getDomainTableColumn(localize),
|
||||
area: {
|
||||
title: localize("ui.panel.config.voice_assistants.expose.headers.area"),
|
||||
sortable: true,
|
||||
groupable: true,
|
||||
filterable: true,
|
||||
template: (entry) => entry.area || "—",
|
||||
},
|
||||
entity_id: {
|
||||
title: localize(
|
||||
"ui.panel.config.voice_assistants.expose.headers.entity_id"
|
||||
),
|
||||
sortable: true,
|
||||
filterable: true,
|
||||
defaultHidden: true,
|
||||
},
|
||||
domain: {
|
||||
title: localize(
|
||||
"ui.panel.config.voice_assistants.expose.headers.domain"
|
||||
),
|
||||
sortable: false,
|
||||
hidden: true,
|
||||
filterable: true,
|
||||
groupable: true,
|
||||
},
|
||||
assistants: getAssistantsTableColumn(
|
||||
localize,
|
||||
this.hass,
|
||||
|
||||
@@ -699,11 +699,6 @@ class PanelEnergy extends LitElement {
|
||||
var(--safe-area-inset-left, 0px)
|
||||
);
|
||||
inset-inline-end: var(--safe-area-inset-right, 0);
|
||||
transition:
|
||||
left var(--ha-animation-duration-normal) ease,
|
||||
right var(--ha-animation-duration-normal) ease,
|
||||
inset-inline-start var(--ha-animation-duration-normal) ease,
|
||||
inset-inline-end var(--ha-animation-duration-normal) ease;
|
||||
margin: 0 auto;
|
||||
max-width: calc(min(470px, 100% - var(--ha-space-4)));
|
||||
box-sizing: border-box;
|
||||
|
||||
@@ -47,70 +47,39 @@ export class DialogNewOverview
|
||||
<ha-wa-dialog
|
||||
.hass=${this.hass}
|
||||
.open=${this._open}
|
||||
.headerTitle=${this.hass.localize(
|
||||
"ui.panel.home.new_overview_dialog.title"
|
||||
)}
|
||||
header-title="Welcome to your new overview"
|
||||
prevent-scrim-close
|
||||
@closed=${this._dialogClosed}
|
||||
>
|
||||
<div class="content">
|
||||
<p>
|
||||
${this.hass.localize(
|
||||
"ui.panel.home.new_overview_dialog.description"
|
||||
)}
|
||||
The overview dashboard has been redesigned to give you a better
|
||||
experience managing your smart home.
|
||||
</p>
|
||||
<h3>
|
||||
${this.hass.localize("ui.panel.home.new_overview_dialog.whats_new")}
|
||||
</h3>
|
||||
<h3>What's new</h3>
|
||||
<ul>
|
||||
<li>
|
||||
<strong>
|
||||
${this.hass.localize(
|
||||
"ui.panel.home.new_overview_dialog.automatic_organization"
|
||||
)}
|
||||
</strong>
|
||||
-
|
||||
${this.hass.localize(
|
||||
"ui.panel.home.new_overview_dialog.automatic_organization_description"
|
||||
)}
|
||||
<strong>Automatic organization</strong> - Your devices are now
|
||||
automatically organized by area and floor.
|
||||
</li>
|
||||
<li>
|
||||
<strong>
|
||||
${this.hass.localize(
|
||||
"ui.panel.home.new_overview_dialog.favorites"
|
||||
)}
|
||||
</strong>
|
||||
-
|
||||
${this.hass.localize(
|
||||
"ui.panel.home.new_overview_dialog.favorites_description"
|
||||
)}
|
||||
<strong>Favorites</strong> - Pin your most used entities to the
|
||||
top for quick access.
|
||||
</li>
|
||||
</ul>
|
||||
<h3>
|
||||
${this.hass.localize(
|
||||
"ui.panel.home.new_overview_dialog.existing_dashboards"
|
||||
)}
|
||||
</h3>
|
||||
<h3>Your existing dashboards</h3>
|
||||
<p>
|
||||
${this.hass.localize(
|
||||
"ui.panel.home.new_overview_dialog.existing_dashboards_description",
|
||||
{
|
||||
dashboard_settings: html`<a
|
||||
href="/config/lovelace/dashboards"
|
||||
@click=${this.closeDialog}
|
||||
>${this.hass.localize(
|
||||
"ui.panel.home.new_overview_dialog.dashboard_settings"
|
||||
)}</a
|
||||
>`,
|
||||
}
|
||||
)}
|
||||
Your manual dashboards are still available in the sidebar. This new
|
||||
overview works alongside them. You can also create a new dashboard
|
||||
using the "Overview (legacy)" template in
|
||||
<a href="/config/lovelace/dashboards" @click=${this.closeDialog}
|
||||
>dashboard settings</a
|
||||
>.
|
||||
</p>
|
||||
</div>
|
||||
<ha-dialog-footer slot="footer">
|
||||
<ha-button slot="primaryAction" @click=${this.closeDialog}>
|
||||
${this.hass.localize(
|
||||
"ui.panel.home.new_overview_dialog.ok_understood"
|
||||
)}
|
||||
OK, understood
|
||||
</ha-button>
|
||||
</ha-dialog-footer>
|
||||
</ha-wa-dialog>
|
||||
|
||||
@@ -276,12 +276,12 @@ class PanelHome extends LitElement {
|
||||
<div class="banner-content">
|
||||
<ha-svg-icon .path=${mdiHomeAssistant}></ha-svg-icon>
|
||||
<span class="banner-text">
|
||||
${this.hass.localize("ui.panel.home.banner.welcome_message")}
|
||||
Welcome to the new overview dashboard.
|
||||
</span>
|
||||
</div>
|
||||
<div class="banner-actions">
|
||||
<ha-button size="small" appearance="filled" @click=${this._learnMore}>
|
||||
${this.hass.localize("ui.panel.home.banner.learn_more")}
|
||||
Learn more
|
||||
</ha-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -186,6 +186,7 @@ class PanelLight extends LitElement {
|
||||
);
|
||||
padding-top: var(--safe-area-inset-top);
|
||||
z-index: 4;
|
||||
transition: box-shadow 200ms linear;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
-webkit-backdrop-filter: var(--app-header-backdrop-filter, none);
|
||||
|
||||
@@ -155,7 +155,6 @@ export class HuiHeadingCard extends LitElement implements LovelaceCard {
|
||||
align-items: center;
|
||||
overflow: visible;
|
||||
gap: var(--ha-space-2);
|
||||
max-height: 100%;
|
||||
}
|
||||
.content:hover ha-icon-next {
|
||||
transform: translateX(calc(4px * var(--scale-direction)));
|
||||
@@ -223,9 +222,6 @@ export class HuiHeadingCard extends LitElement implements LovelaceCard {
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
gap: 4px 10px;
|
||||
flex-wrap: wrap;
|
||||
max-height: 100%;
|
||||
overflow-y: clip;
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -1,15 +1,19 @@
|
||||
import type { PropertyValues } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { customElement, property, query } from "lit/decorators";
|
||||
import { refine } from "superstruct";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import { stopPropagation } from "../../../common/dom/stop_propagation";
|
||||
import "../../../components/ha-assist-pipeline-picker";
|
||||
import type {
|
||||
HaFormSchema,
|
||||
SchemaUnion,
|
||||
} from "../../../components/ha-form/types";
|
||||
import "../../../components/ha-help-tooltip";
|
||||
import "../../../components/ha-list-item";
|
||||
import "../../../components/ha-navigation-picker";
|
||||
import type { HaSelect } from "../../../components/ha-select";
|
||||
import "../../../components/ha-service-control";
|
||||
import type {
|
||||
ActionConfig,
|
||||
@@ -88,6 +92,8 @@ export class HuiActionEditor extends LitElement {
|
||||
@property({ attribute: false })
|
||||
public context?: ActionRelatedContext;
|
||||
|
||||
@query("ha-select") private _select!: HaSelect;
|
||||
|
||||
get _navigation_path(): string {
|
||||
const config = this.config as NavigateActionConfig | undefined;
|
||||
return config?.navigation_path || "";
|
||||
@@ -130,6 +136,15 @@ export class HuiActionEditor extends LitElement {
|
||||
]
|
||||
);
|
||||
|
||||
protected updated(changedProperties: PropertyValues<typeof this>) {
|
||||
super.updated(changedProperties);
|
||||
if (changedProperties.has("defaultAction")) {
|
||||
if (changedProperties.get("defaultAction") !== this.defaultAction) {
|
||||
this._select.layoutOptions();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected render() {
|
||||
if (!this.hass) {
|
||||
return nothing;
|
||||
@@ -150,28 +165,29 @@ export class HuiActionEditor extends LitElement {
|
||||
.configValue=${"action"}
|
||||
@selected=${this._actionPicked}
|
||||
.value=${action}
|
||||
.options=${[
|
||||
{
|
||||
value: "default",
|
||||
label: `${this.hass!.localize(
|
||||
"ui.panel.lovelace.editor.action-editor.actions.default_action"
|
||||
)}
|
||||
${
|
||||
this.defaultAction
|
||||
? ` (${this.hass!.localize(
|
||||
`ui.panel.lovelace.editor.action-editor.actions.${this.defaultAction}`
|
||||
).toLowerCase()})`
|
||||
: ""
|
||||
}`,
|
||||
},
|
||||
...actions.map((actn) => ({
|
||||
value: actn,
|
||||
label: this.hass!.localize(
|
||||
`ui.panel.lovelace.editor.action-editor.actions.${actn}`
|
||||
),
|
||||
})),
|
||||
]}
|
||||
@closed=${stopPropagation}
|
||||
fixedMenuPosition
|
||||
naturalMenuWidth
|
||||
>
|
||||
<ha-list-item value="default">
|
||||
${this.hass!.localize(
|
||||
"ui.panel.lovelace.editor.action-editor.actions.default_action"
|
||||
)}
|
||||
${this.defaultAction
|
||||
? ` (${this.hass!.localize(
|
||||
`ui.panel.lovelace.editor.action-editor.actions.${this.defaultAction}`
|
||||
).toLowerCase()})`
|
||||
: nothing}
|
||||
</ha-list-item>
|
||||
${actions.map(
|
||||
(actn) => html`
|
||||
<ha-list-item .value=${actn}>
|
||||
${this.hass!.localize(
|
||||
`ui.panel.lovelace.editor.action-editor.actions.${actn}`
|
||||
)}
|
||||
</ha-list-item>
|
||||
`
|
||||
)}
|
||||
</ha-select>
|
||||
${this.tooltipText
|
||||
? html`
|
||||
@@ -233,7 +249,7 @@ export class HuiActionEditor extends LitElement {
|
||||
`;
|
||||
}
|
||||
|
||||
private _actionPicked(ev: CustomEvent<{ value: string }>): void {
|
||||
private _actionPicked(ev): void {
|
||||
ev.stopPropagation();
|
||||
if (!this.hass) {
|
||||
return;
|
||||
@@ -244,7 +260,7 @@ export class HuiActionEditor extends LitElement {
|
||||
action = "perform-action";
|
||||
}
|
||||
|
||||
const value = ev.detail.value;
|
||||
const value = ev.target.value;
|
||||
|
||||
if (action === value) {
|
||||
return;
|
||||
|
||||
@@ -545,7 +545,7 @@ export class HuiEnergyPeriodSelector extends SubscribeMixin(LitElement) {
|
||||
);
|
||||
pointer-events: none;
|
||||
opacity: 0;
|
||||
transition: opacity var(--ha-animation-duration-slow) ease-in-out;
|
||||
transition: opacity var(--ha-animation-base-duration) ease-in-out;
|
||||
}
|
||||
.datepicker-open .backdrop {
|
||||
opacity: 1;
|
||||
|
||||
@@ -37,7 +37,6 @@ import type { GUIModeChangedEvent } from "../types";
|
||||
import "./hui-badge-element-editor";
|
||||
import type { HuiBadgeElementEditor } from "./hui-badge-element-editor";
|
||||
import type { EditBadgeDialogParams } from "./show-edit-badge-dialog";
|
||||
import { withViewTransition } from "../../../../common/util/view-transition";
|
||||
|
||||
declare global {
|
||||
// for fire event
|
||||
@@ -300,9 +299,7 @@ export class HuiDialogEditBadge
|
||||
}
|
||||
|
||||
private _enlarge() {
|
||||
withViewTransition(() => {
|
||||
this.large = !this.large;
|
||||
});
|
||||
this.large = !this.large;
|
||||
}
|
||||
|
||||
private _ignoreKeydown(ev: KeyboardEvent) {
|
||||
|
||||
@@ -36,7 +36,6 @@ import type { GUIModeChangedEvent } from "../types";
|
||||
import "./hui-card-element-editor";
|
||||
import type { HuiCardElementEditor } from "./hui-card-element-editor";
|
||||
import type { EditCardDialogParams } from "./show-edit-card-dialog";
|
||||
import { withViewTransition } from "../../../../common/util/view-transition";
|
||||
|
||||
declare global {
|
||||
// for fire event
|
||||
@@ -268,9 +267,7 @@ export class HuiDialogEditCard
|
||||
}
|
||||
|
||||
private _enlarge() {
|
||||
withViewTransition(() => {
|
||||
this.large = !this.large;
|
||||
});
|
||||
this.large = !this.large;
|
||||
}
|
||||
|
||||
private _ignoreKeydown(ev: KeyboardEvent) {
|
||||
|
||||
@@ -1,18 +1,13 @@
|
||||
import {
|
||||
mdiClose,
|
||||
mdiContentDuplicate,
|
||||
mdiPencil,
|
||||
mdiPlaylistPlus,
|
||||
} from "@mdi/js";
|
||||
import { mdiClose, mdiContentDuplicate, mdiPencil } from "@mdi/js";
|
||||
import deepClone from "deep-clone-simple";
|
||||
import { LitElement, css, html, nothing } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { customElement, property, query } from "lit/decorators";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import "../../../components/ha-button";
|
||||
import "../../../components/ha-dropdown";
|
||||
import type { HaDropdownSelectEvent } from "../../../components/ha-dropdown";
|
||||
import "../../../components/ha-dropdown-item";
|
||||
import { stopPropagation } from "../../../common/dom/stop_propagation";
|
||||
import "../../../components/ha-icon-button";
|
||||
import "../../../components/ha-list-item";
|
||||
import "../../../components/ha-select";
|
||||
import type { HaSelect } from "../../../components/ha-select";
|
||||
import "../../../components/ha-svg-icon";
|
||||
import { showConfirmationDialog } from "../../../dialogs/generic/show-dialog-box";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
@@ -52,6 +47,8 @@ export class HuiPictureElementsCardRowEditor extends LitElement {
|
||||
|
||||
@property({ attribute: false }) public elements?: LovelaceElementConfig[];
|
||||
|
||||
@query("ha-select") private _select!: HaSelect;
|
||||
|
||||
protected render() {
|
||||
if (!this.elements || !this.hass) {
|
||||
return nothing;
|
||||
@@ -107,23 +104,26 @@ export class HuiPictureElementsCardRowEditor extends LitElement {
|
||||
</div>
|
||||
`
|
||||
)}
|
||||
<ha-dropdown @wa-select=${this._addElement}>
|
||||
<ha-button size="small" slot="trigger" appearance="filled">
|
||||
<ha-svg-icon slot="start" .path=${mdiPlaylistPlus}></ha-svg-icon>
|
||||
${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.picture-elements.new_element"
|
||||
)}
|
||||
</ha-button>
|
||||
<ha-select
|
||||
fixedMenuPosition
|
||||
naturalMenuWidth
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.picture-elements.new_element"
|
||||
)}
|
||||
.value=${""}
|
||||
@closed=${stopPropagation}
|
||||
@selected=${this._addElement}
|
||||
>
|
||||
${elementTypes.map(
|
||||
(element) => html`
|
||||
<ha-dropdown-item .value=${element}>
|
||||
${this.hass?.localize(
|
||||
<ha-list-item .value=${element}
|
||||
>${this.hass?.localize(
|
||||
`ui.panel.lovelace.editor.card.picture-elements.element_types.${element}`
|
||||
) || element}
|
||||
</ha-dropdown-item>
|
||||
)}</ha-list-item
|
||||
>
|
||||
`
|
||||
)}
|
||||
</ha-dropdown>
|
||||
</ha-select>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
@@ -177,8 +177,8 @@ export class HuiPictureElementsCardRowEditor extends LitElement {
|
||||
return element.title ?? "Unknown type";
|
||||
}
|
||||
|
||||
private async _addElement(ev: HaDropdownSelectEvent): Promise<void> {
|
||||
const value = ev.detail.item.value;
|
||||
private async _addElement(ev): Promise<void> {
|
||||
const value = ev.target!.value;
|
||||
if (value === "") {
|
||||
return;
|
||||
}
|
||||
@@ -191,6 +191,7 @@ export class HuiPictureElementsCardRowEditor extends LitElement {
|
||||
)
|
||||
);
|
||||
fireEvent(this, "elements-changed", { elements: newElements });
|
||||
this._select.select(-1);
|
||||
}
|
||||
|
||||
private _removeRow(ev: CustomEvent): void {
|
||||
@@ -268,6 +269,10 @@ export class HuiPictureElementsCardRowEditor extends LitElement {
|
||||
font-size: var(--ha-font-size-s);
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
|
||||
ha-select {
|
||||
width: 100%;
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,11 +2,13 @@ import type { CSSResultGroup } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, state } from "lit/decorators";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import { stopPropagation } from "../../../../common/dom/stop_propagation";
|
||||
import "../../../../components/ha-alert";
|
||||
import "../../../../components/ha-button";
|
||||
import { createCloseHeading } from "../../../../components/ha-dialog";
|
||||
import "../../../../components/ha-icon";
|
||||
import "../../../../components/ha-list";
|
||||
import "../../../../components/ha-list-item";
|
||||
import "../../../../components/ha-radio-list-item";
|
||||
import "../../../../components/ha-select";
|
||||
import type { LovelaceConfig } from "../../../../data/lovelace/config/types";
|
||||
@@ -80,23 +82,29 @@ export class HuiDialogSelectView extends LitElement {
|
||||
.disabled=${!this._dashboards.length}
|
||||
.value=${this._urlPath || defaultPanel}
|
||||
@selected=${this._dashboardChanged}
|
||||
.options=${this._dashboards
|
||||
.map((dashboard) => ({
|
||||
value: dashboard.url_path,
|
||||
label: `${dashboard.title}${dashboard.id === "lovelace" ? ` (${this.hass.localize("ui.common.default")})` : ""}`,
|
||||
disabled: dashboard.mode !== "storage",
|
||||
}))
|
||||
.sort((a, b) =>
|
||||
a.value === "lovelace"
|
||||
? -1
|
||||
: b.value === "lovelace"
|
||||
? 1
|
||||
: a.label.localeCompare(b.label)
|
||||
)}
|
||||
@closed=${stopPropagation}
|
||||
fixedMenuPosition
|
||||
naturalMenuWidth
|
||||
dialogInitialFocus
|
||||
>
|
||||
<ha-list-item
|
||||
value="lovelace"
|
||||
.disabled=${(this.hass.panels.lovelace?.config as any)?.mode ===
|
||||
"yaml"}
|
||||
>
|
||||
Default
|
||||
</ha-list-item>
|
||||
${this._dashboards.map(
|
||||
(dashboard) => html`
|
||||
<ha-list-item
|
||||
.disabled=${dashboard.mode !== "storage"}
|
||||
.value=${dashboard.url_path}
|
||||
>${dashboard.title}</ha-list-item
|
||||
>
|
||||
`
|
||||
)}
|
||||
</ha-select>`
|
||||
: nothing}
|
||||
: ""}
|
||||
${!this._config || (this._config.views || []).length < 1
|
||||
? html`<ha-alert alert-type="error"
|
||||
>${this.hass.localize(
|
||||
@@ -134,7 +142,7 @@ export class HuiDialogSelectView extends LitElement {
|
||||
})}
|
||||
</ha-list>
|
||||
`
|
||||
: nothing}
|
||||
: ""}
|
||||
<ha-button
|
||||
slot="primaryAction"
|
||||
@click=${this.closeDialog}
|
||||
@@ -159,8 +167,8 @@ export class HuiDialogSelectView extends LitElement {
|
||||
this._params!.dashboards || (await fetchDashboards(this.hass));
|
||||
}
|
||||
|
||||
private async _dashboardChanged(ev: CustomEvent<{ value: string }>) {
|
||||
let urlPath: string | null = ev.detail.value;
|
||||
private async _dashboardChanged(ev) {
|
||||
let urlPath: string | null = ev.target.value;
|
||||
if (urlPath === this._urlPath) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import type { PropertyValues } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { stopPropagation } from "../../../common/dom/stop_propagation";
|
||||
import "../../../components/ha-list-item";
|
||||
import "../../../components/ha-select";
|
||||
import { UNAVAILABLE } from "../../../data/entity/entity";
|
||||
import { forwardHaptic } from "../../../data/haptics";
|
||||
@@ -68,8 +70,17 @@ class HuiInputSelectEntityRow extends LitElement implements LovelaceRow {
|
||||
.disabled=${
|
||||
stateObj.state === UNAVAILABLE /* UNKNOWN state is allowed */
|
||||
}
|
||||
naturalMenuWidth
|
||||
@selected=${this._selectedChanged}
|
||||
@click=${stopPropagation}
|
||||
@closed=${stopPropagation}
|
||||
>
|
||||
${stateObj.attributes.options
|
||||
? stateObj.attributes.options.map(
|
||||
(option) =>
|
||||
html`<ha-list-item .value=${option}>${option}</ha-list-item>`
|
||||
)
|
||||
: ""}
|
||||
</ha-select>
|
||||
</hui-generic-entity-row>
|
||||
`;
|
||||
@@ -86,11 +97,11 @@ class HuiInputSelectEntityRow extends LitElement implements LovelaceRow {
|
||||
}
|
||||
`;
|
||||
|
||||
private _selectedChanged(ev: CustomEvent<{ value: string }>): void {
|
||||
private _selectedChanged(ev): void {
|
||||
const stateObj = this.hass!.states[
|
||||
this._config!.entity
|
||||
] as InputSelectEntity;
|
||||
const option = ev.detail.value;
|
||||
const option = ev.target.value;
|
||||
if (
|
||||
option === stateObj.state ||
|
||||
!stateObj.attributes.options.includes(option)
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import type { PropertyValues } from "lit";
|
||||
import { LitElement, css, html, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { stopPropagation } from "../../../common/dom/stop_propagation";
|
||||
import "../../../components/ha-list-item";
|
||||
import "../../../components/ha-select";
|
||||
import { UNAVAILABLE } from "../../../data/entity/entity";
|
||||
import { forwardHaptic } from "../../../data/haptics";
|
||||
@@ -20,8 +22,6 @@ class HuiSelectEntityRow extends LitElement implements LovelaceRow {
|
||||
|
||||
@state() private _config?: EntitiesCardEntityConfig;
|
||||
|
||||
@state() private _selectedEntityRow?: string;
|
||||
|
||||
public setConfig(config: EntitiesCardEntityConfig): void {
|
||||
if (!config || !config.entity) {
|
||||
throw new Error("Entity must be specified");
|
||||
@@ -65,14 +65,23 @@ class HuiSelectEntityRow extends LitElement implements LovelaceRow {
|
||||
>
|
||||
<ha-select
|
||||
.label=${name}
|
||||
.value=${this._selectedEntityRow || stateObj.state}
|
||||
.options=${stateObj.attributes.options?.map((option) => ({
|
||||
value: option,
|
||||
label: this.hass!.formatEntityState(stateObj, option),
|
||||
}))}
|
||||
.value=${stateObj.state}
|
||||
.options=${stateObj.attributes.options}
|
||||
.disabled=${stateObj.state === UNAVAILABLE}
|
||||
@selected=${this._handleAction}
|
||||
naturalMenuWidth
|
||||
@action=${this._handleAction}
|
||||
@click=${stopPropagation}
|
||||
@closed=${stopPropagation}
|
||||
>
|
||||
${stateObj.attributes.options
|
||||
? stateObj.attributes.options.map(
|
||||
(option) => html`
|
||||
<ha-list-item .value=${option}>
|
||||
${this.hass!.formatEntityState(stateObj, option)}
|
||||
</ha-list-item>
|
||||
`
|
||||
)
|
||||
: ""}
|
||||
</ha-select>
|
||||
</hui-generic-entity-row>
|
||||
`;
|
||||
@@ -89,10 +98,10 @@ class HuiSelectEntityRow extends LitElement implements LovelaceRow {
|
||||
}
|
||||
`;
|
||||
|
||||
private _handleAction(ev: CustomEvent<{ value: string }>): void {
|
||||
private _handleAction(ev): void {
|
||||
const stateObj = this.hass!.states[this._config!.entity] as SelectEntity;
|
||||
|
||||
const option = ev.detail.value;
|
||||
const option = ev.target.value;
|
||||
|
||||
if (
|
||||
option === stateObj.state ||
|
||||
@@ -111,7 +120,9 @@ class HuiSelectEntityRow extends LitElement implements LovelaceRow {
|
||||
setTimeout(() => {
|
||||
const newStateObj = this.hass!.states[this._config!.entity];
|
||||
if (newStateObj === stateObj) {
|
||||
this._selectedEntityRow = stateObj.state;
|
||||
const select = this.shadowRoot?.querySelector("ha-select");
|
||||
const index = select?.options.indexOf(stateObj.state) ?? -1;
|
||||
select?.select(index);
|
||||
}
|
||||
}, 2000)
|
||||
);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user