mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-10 02:46:38 +00:00
20240430.0 (#20681)
This commit is contained in:
commit
ef4f11fdf8
@ -1,19 +1,19 @@
|
||||
import { mdiStorePlus, mdiUpdate } from "@mdi/js";
|
||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { mdiRefresh, mdiStorePlus } from "@mdi/js";
|
||||
import { CSSResultGroup, LitElement, TemplateResult, css, html } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { atLeastVersion } from "../../../src/common/config/version";
|
||||
import { fireEvent } from "../../../src/common/dom/fire_event";
|
||||
import "../../../src/components/ha-fab";
|
||||
import { reloadHassioAddons } from "../../../src/data/hassio/addon";
|
||||
import { extractApiErrorMessage } from "../../../src/data/hassio/common";
|
||||
import { Supervisor } from "../../../src/data/supervisor/supervisor";
|
||||
import { showAlertDialog } from "../../../src/dialogs/generic/show-dialog-box";
|
||||
import "../../../src/layouts/hass-subpage";
|
||||
import "../../../src/layouts/hass-tabs-subpage";
|
||||
import { haStyle } from "../../../src/resources/styles";
|
||||
import { HomeAssistant, Route } from "../../../src/types";
|
||||
import { supervisorTabs } from "../hassio-tabs";
|
||||
import "./hassio-addons";
|
||||
import "../../../src/layouts/hass-subpage";
|
||||
import { reloadHassioAddons } from "../../../src/data/hassio/addon";
|
||||
import { extractApiErrorMessage } from "../../../src/data/hassio/common";
|
||||
import { showAlertDialog } from "../../../src/dialogs/generic/show-dialog-box";
|
||||
import { fireEvent } from "../../../src/common/dom/fire_event";
|
||||
|
||||
@customElement("hassio-dashboard")
|
||||
class HassioDashboard extends LitElement {
|
||||
@ -43,7 +43,7 @@ class HassioDashboard extends LitElement {
|
||||
<ha-icon-button
|
||||
slot="toolbar-icon"
|
||||
@click=${this._handleCheckUpdates}
|
||||
.path=${mdiUpdate}
|
||||
.path=${mdiRefresh}
|
||||
.label=${this.supervisor.localize("store.check_updates")}
|
||||
></ha-icon-button>
|
||||
<hassio-addons
|
||||
|
@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "home-assistant-frontend"
|
||||
version = "20240429.0"
|
||||
version = "20240430.0"
|
||||
license = {text = "Apache-2.0"}
|
||||
description = "The Home Assistant frontend"
|
||||
readme = "README.md"
|
||||
|
@ -61,11 +61,8 @@ export const applyThemesOnElement = (
|
||||
const accentColor = themeSettings?.accentColor;
|
||||
|
||||
if (darkMode && primaryColor) {
|
||||
themeRules["app-header-background-color"] = hexBlend(
|
||||
primaryColor,
|
||||
"#121212",
|
||||
8
|
||||
);
|
||||
themeRules["app-theme-color"] = hexBlend(primaryColor, "#121212", 8);
|
||||
themeRules["app-header-background-color"] = themeRules["app-theme-color"];
|
||||
}
|
||||
|
||||
if (primaryColor) {
|
||||
|
@ -127,6 +127,10 @@ export class HaDialog extends DialogBase {
|
||||
border-radius: var(--ha-dialog-border-radius, 28px);
|
||||
-webkit-backdrop-filter: var(--ha-dialog-surface-backdrop-filter, none);
|
||||
backdrop-filter: var(--ha-dialog-surface-backdrop-filter, none);
|
||||
background: var(
|
||||
--ha-dialog-surface-background,
|
||||
var(--mdc-theme-surface, #fff)
|
||||
);
|
||||
}
|
||||
:host([flexContent]) .mdc-dialog .mdc-dialog__content {
|
||||
display: flex;
|
||||
|
@ -54,7 +54,7 @@ export class HaSettingsRow extends LitElement {
|
||||
.body[three-line] {
|
||||
min-height: var(--paper-item-body-three-line-min-height, 88px);
|
||||
}
|
||||
:host(:not([wrap-heading])) > * {
|
||||
:host(:not([wrap-heading])) body > * {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
|
@ -44,6 +44,7 @@ export interface IntegrationManifest {
|
||||
| "local_polling"
|
||||
| "local_push";
|
||||
single_config_entry?: boolean;
|
||||
version?: string;
|
||||
}
|
||||
export interface IntegrationSetup {
|
||||
domain: string;
|
||||
|
@ -118,6 +118,10 @@ export const checkForEntityUpdates = async (
|
||||
return;
|
||||
}
|
||||
|
||||
showToast(element, {
|
||||
message: hass.localize("ui.panel.config.updates.checking_updates"),
|
||||
});
|
||||
|
||||
let updated = 0;
|
||||
|
||||
const unsubscribeEvents = await hass.connection.subscribeEvents<HassEvent>(
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { RequestSelectedDetail } from "@material/mwc-list/mwc-list-item";
|
||||
import { mdiDotsVertical, mdiUpdate } from "@mdi/js";
|
||||
import { mdiDotsVertical, mdiRefresh } from "@mdi/js";
|
||||
import { HassEntities } from "home-assistant-js-websocket";
|
||||
import { css, html, LitElement, TemplateResult } from "lit";
|
||||
import { LitElement, TemplateResult, css, html } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
|
||||
@ -14,11 +14,11 @@ import "../../../components/ha-check-list-item";
|
||||
import "../../../components/ha-metric";
|
||||
import { extractApiErrorMessage } from "../../../data/hassio/common";
|
||||
import {
|
||||
fetchHassioSupervisorInfo,
|
||||
HassioSupervisorInfo,
|
||||
SupervisorOptions,
|
||||
fetchHassioSupervisorInfo,
|
||||
reloadSupervisor,
|
||||
setSupervisorOption,
|
||||
SupervisorOptions,
|
||||
} from "../../../data/hassio/supervisor";
|
||||
import {
|
||||
checkForEntityUpdates,
|
||||
@ -66,7 +66,7 @@ class HaConfigSectionUpdates extends LitElement {
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.config.updates.check_updates"
|
||||
)}
|
||||
.path=${mdiUpdate}
|
||||
.path=${mdiRefresh}
|
||||
@click=${this._checkUpdates}
|
||||
></ha-icon-button>
|
||||
<ha-button-menu multi>
|
||||
|
@ -4,7 +4,7 @@ import {
|
||||
mdiDotsVertical,
|
||||
mdiMagnify,
|
||||
mdiPower,
|
||||
mdiUpdate,
|
||||
mdiRefresh,
|
||||
} from "@mdi/js";
|
||||
import { HassEntities, UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||
import {
|
||||
@ -206,7 +206,7 @@ class HaConfigDashboard extends SubscribeMixin(LitElement) {
|
||||
|
||||
<ha-list-item graphic="icon">
|
||||
${this.hass.localize("ui.panel.config.updates.check_updates")}
|
||||
<ha-svg-icon slot="graphic" .path=${mdiUpdate}></ha-svg-icon>
|
||||
<ha-svg-icon slot="graphic" .path=${mdiRefresh}></ha-svg-icon>
|
||||
</ha-list-item>
|
||||
|
||||
<ha-list-item graphic="icon">
|
||||
|
@ -116,6 +116,9 @@ export interface EntityRow extends StateEntity {
|
||||
localized_platform: string;
|
||||
domain: string;
|
||||
label_entries: LabelRegistryEntry[];
|
||||
enabled: string;
|
||||
visible: string;
|
||||
available: string;
|
||||
}
|
||||
|
||||
@customElement("ha-config-entities")
|
||||
@ -198,20 +201,36 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
|
||||
|
||||
private _states = memoize((localize: LocalizeFunc) => [
|
||||
{
|
||||
value: "disabled",
|
||||
label: localize("ui.panel.config.entities.picker.status.disabled"),
|
||||
},
|
||||
{
|
||||
value: "hidden",
|
||||
label: localize("ui.panel.config.entities.picker.status.hidden"),
|
||||
value: "available",
|
||||
label: localize("ui.panel.config.entities.picker.status.available"),
|
||||
},
|
||||
{
|
||||
value: "unavailable",
|
||||
label: localize("ui.panel.config.entities.picker.status.unavailable"),
|
||||
},
|
||||
{
|
||||
value: "enabled",
|
||||
label: localize("ui.panel.config.entities.picker.status.enabled"),
|
||||
},
|
||||
{
|
||||
value: "disabled",
|
||||
label: localize("ui.panel.config.entities.picker.status.disabled"),
|
||||
},
|
||||
{
|
||||
value: "visible",
|
||||
label: localize("ui.panel.config.entities.picker.status.visible"),
|
||||
},
|
||||
{
|
||||
value: "hidden",
|
||||
label: localize("ui.panel.config.entities.picker.status.hidden"),
|
||||
},
|
||||
{
|
||||
value: "readonly",
|
||||
label: localize("ui.panel.config.entities.picker.status.readonly"),
|
||||
label: localize("ui.panel.config.entities.picker.status.unmanageable"),
|
||||
},
|
||||
{
|
||||
value: "restored",
|
||||
label: localize("ui.panel.config.entities.picker.status.not_provided"),
|
||||
},
|
||||
]);
|
||||
|
||||
@ -310,7 +329,6 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
|
||||
type: "icon",
|
||||
sortable: true,
|
||||
filterable: true,
|
||||
groupable: true,
|
||||
width: "68px",
|
||||
template: (entry) =>
|
||||
entry.unavailable ||
|
||||
@ -339,7 +357,7 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
|
||||
<simple-tooltip animation-delay="0" position="left">
|
||||
${entry.restored
|
||||
? this.hass.localize(
|
||||
"ui.panel.config.entities.picker.status.restored"
|
||||
"ui.panel.config.entities.picker.status.not_provided"
|
||||
)
|
||||
: entry.unavailable
|
||||
? this.hass.localize(
|
||||
@ -354,13 +372,31 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
|
||||
"ui.panel.config.entities.picker.status.hidden"
|
||||
)
|
||||
: this.hass.localize(
|
||||
"ui.panel.config.entities.picker.status.readonly"
|
||||
"ui.panel.config.entities.picker.status.unmanageable"
|
||||
)}
|
||||
</simple-tooltip>
|
||||
</div>
|
||||
`
|
||||
: "—",
|
||||
},
|
||||
available: {
|
||||
title: localize("ui.panel.config.entities.picker.headers.availability"),
|
||||
sortable: true,
|
||||
groupable: true,
|
||||
hidden: true,
|
||||
},
|
||||
visible: {
|
||||
title: localize("ui.panel.config.entities.picker.headers.visibility"),
|
||||
sortable: true,
|
||||
groupable: true,
|
||||
hidden: true,
|
||||
},
|
||||
enabled: {
|
||||
title: localize("ui.panel.config.entities.picker.headers.enabled"),
|
||||
sortable: true,
|
||||
groupable: true,
|
||||
hidden: true,
|
||||
},
|
||||
labels: {
|
||||
title: "",
|
||||
hidden: true,
|
||||
@ -389,18 +425,24 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
|
||||
|
||||
const stateFilters = filters["ha-filter-states"]?.value;
|
||||
|
||||
const showReadOnly =
|
||||
!stateFilters?.length || stateFilters.includes("readonly");
|
||||
const showEnabled =
|
||||
!stateFilters?.length || stateFilters.includes("enabled");
|
||||
const showDisabled =
|
||||
!stateFilters?.length || stateFilters.includes("disabled");
|
||||
const showVisible =
|
||||
!stateFilters?.length || stateFilters.includes("visible");
|
||||
const showHidden =
|
||||
!stateFilters?.length || stateFilters.includes("hidden");
|
||||
const showAvailable =
|
||||
!stateFilters?.length || stateFilters.includes("available");
|
||||
const showUnavailable =
|
||||
!stateFilters?.length || stateFilters.includes("unavailable");
|
||||
const showRestored =
|
||||
!stateFilters?.length || stateFilters.includes("restored");
|
||||
const showReadOnly =
|
||||
!stateFilters?.length || stateFilters.includes("readonly");
|
||||
|
||||
let filteredEntities = showReadOnly
|
||||
? entities.concat(stateEntities)
|
||||
: entities;
|
||||
let filteredEntities = entities.concat(stateEntities);
|
||||
|
||||
let filteredConfigEntry: ConfigEntry | undefined;
|
||||
const filteredDomains = new Set<string>();
|
||||
@ -459,26 +501,29 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
|
||||
}
|
||||
});
|
||||
|
||||
if (!showDisabled) {
|
||||
filteredEntities = filteredEntities.filter(
|
||||
(entity) => !entity.disabled_by
|
||||
);
|
||||
}
|
||||
|
||||
if (!showHidden) {
|
||||
filteredEntities = filteredEntities.filter(
|
||||
(entity) => !entity.hidden_by
|
||||
);
|
||||
}
|
||||
|
||||
for (const entry of filteredEntities) {
|
||||
const entity = this.hass.states[entry.entity_id];
|
||||
const unavailable = entity?.state === UNAVAILABLE;
|
||||
const restored = entity?.attributes.restored === true;
|
||||
const areaId = entry.area_id ?? devices[entry.device_id!]?.area_id;
|
||||
const area = areaId ? areas[areaId] : undefined;
|
||||
const hidden = !!entry.hidden_by;
|
||||
const disabled = !!entry.disabled_by;
|
||||
const readonly = entry.readonly;
|
||||
const available = entity?.state && entity.state !== UNAVAILABLE;
|
||||
|
||||
if (!showUnavailable && unavailable) {
|
||||
if (
|
||||
!(
|
||||
(showAvailable && available) ||
|
||||
(showUnavailable && unavailable) ||
|
||||
(showRestored && restored) ||
|
||||
(showVisible && !hidden) ||
|
||||
(showHidden && hidden) ||
|
||||
(showDisabled && disabled) ||
|
||||
(showEnabled && !disabled) ||
|
||||
(showReadOnly && readonly)
|
||||
)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -500,21 +545,30 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
|
||||
area: area ? area.name : "—",
|
||||
domain: domainToName(localize, computeDomain(entry.entity_id)),
|
||||
status: restored
|
||||
? localize("ui.panel.config.entities.picker.status.restored")
|
||||
? localize("ui.panel.config.entities.picker.status.not_provided")
|
||||
: unavailable
|
||||
? localize("ui.panel.config.entities.picker.status.unavailable")
|
||||
: entry.disabled_by
|
||||
: disabled
|
||||
? localize("ui.panel.config.entities.picker.status.disabled")
|
||||
: entry.hidden_by
|
||||
: hidden
|
||||
? localize("ui.panel.config.entities.picker.status.hidden")
|
||||
: entry.readonly
|
||||
: readonly
|
||||
? localize(
|
||||
"ui.panel.config.entities.picker.status.readonly"
|
||||
"ui.panel.config.entities.picker.status.unmanageable"
|
||||
)
|
||||
: localize(
|
||||
"ui.panel.config.entities.picker.status.available"
|
||||
),
|
||||
label_entries: labelsEntries,
|
||||
available: unavailable
|
||||
? localize("ui.panel.config.entities.picker.status.unavailable")
|
||||
: localize("ui.panel.config.entities.picker.status.available"),
|
||||
enabled: disabled
|
||||
? localize("ui.panel.config.entities.picker.status.disabled")
|
||||
: localize("ui.panel.config.entities.picker.status.enabled"),
|
||||
visible: hidden
|
||||
? localize("ui.panel.config.entities.picker.status.hidden")
|
||||
: localize("ui.panel.config.entities.picker.status.visible"),
|
||||
});
|
||||
}
|
||||
|
||||
@ -861,7 +915,7 @@ ${
|
||||
protected firstUpdated() {
|
||||
this._filters = {
|
||||
"ha-filter-states": {
|
||||
value: ["unavailable", "readonly"],
|
||||
value: ["enabled"],
|
||||
items: undefined,
|
||||
},
|
||||
};
|
||||
@ -876,10 +930,7 @@ ${
|
||||
this._filters = {
|
||||
...this._filters,
|
||||
"ha-filter-states": {
|
||||
value: [
|
||||
...(this._filters["ha-filter-states"]?.value || []),
|
||||
"disabled",
|
||||
],
|
||||
value: [],
|
||||
items: undefined,
|
||||
},
|
||||
"ha-filter-integrations": {
|
||||
@ -892,10 +943,7 @@ ${
|
||||
this._filters = {
|
||||
...this._filters,
|
||||
"ha-filter-states": {
|
||||
value: [
|
||||
...(this._filters["ha-filter-states"]?.value || []),
|
||||
"disabled",
|
||||
],
|
||||
value: [],
|
||||
items: undefined,
|
||||
},
|
||||
config_entry: {
|
||||
|
@ -309,7 +309,7 @@ export class HaConfigHelpers extends SubscribeMixin(LitElement) {
|
||||
<ha-svg-icon .path=${mdiPencilOff}></ha-svg-icon>
|
||||
<simple-tooltip animation-delay="0" position="left">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.entities.picker.status.readonly"
|
||||
"ui.panel.config.entities.picker.status.unmanageable"
|
||||
)}
|
||||
</simple-tooltip>
|
||||
</div>
|
||||
|
@ -269,6 +269,9 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) {
|
||||
@error=${this._onImageError}
|
||||
/>
|
||||
</div>
|
||||
${this._manifest?.version != null
|
||||
? html`<div class="version">${this._manifest.version}</div>`
|
||||
: nothing}
|
||||
${this._manifest?.is_built_in === false
|
||||
? html`<ha-alert alert-type="warning"
|
||||
><ha-svg-icon
|
||||
@ -554,18 +557,22 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) {
|
||||
if (item.error_reason_translation_key) {
|
||||
const lokalisePromExc = this.hass
|
||||
.loadBackendTranslation("exceptions", item.domain)
|
||||
.then((localize) =>
|
||||
localize(
|
||||
`component.${item.domain}.exceptions.${item.error_reason_translation_key}.message`,
|
||||
item.error_reason_translation_placeholders ?? undefined
|
||||
)
|
||||
.then(
|
||||
(localize) =>
|
||||
localize(
|
||||
`component.${item.domain}.exceptions.${item.error_reason_translation_key}.message`,
|
||||
item.error_reason_translation_placeholders ?? undefined
|
||||
) || item.reason
|
||||
);
|
||||
stateTextExtra = html`${until(lokalisePromExc)}`;
|
||||
} else {
|
||||
const lokalisePromError = this.hass
|
||||
.loadBackendTranslation("config", item.domain)
|
||||
.then((localize) =>
|
||||
localize(`component.${item.domain}.config.error.${item.reason}`)
|
||||
.then(
|
||||
(localize) =>
|
||||
localize(
|
||||
`component.${item.domain}.config.error.${item.reason}`
|
||||
) || item.reason
|
||||
);
|
||||
stateTextExtra = html`${until(lokalisePromError, item.reason)}`;
|
||||
}
|
||||
@ -1404,6 +1411,12 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
.version {
|
||||
padding-top: 8px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
.overview .card-actions {
|
||||
padding: 0;
|
||||
}
|
||||
|
@ -112,14 +112,14 @@ class PanelEnergy extends LitElement {
|
||||
</hui-energy-period-selector>
|
||||
</div>
|
||||
</div>
|
||||
<hui-view
|
||||
id="view"
|
||||
.hass=${this.hass}
|
||||
.narrow=${this.narrow}
|
||||
.lovelace=${this._lovelace}
|
||||
.index=${this._viewIndex}
|
||||
@reload-energy-panel=${this._reloadView}
|
||||
></hui-view>
|
||||
<div id="view" @reload-energy-panel=${this._reloadView}>
|
||||
<hui-view
|
||||
.hass=${this.hass}
|
||||
.narrow=${this.narrow}
|
||||
.lovelace=${this._lovelace}
|
||||
.index=${this._viewIndex}
|
||||
></hui-view>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
@ -401,12 +401,10 @@ class PanelEnergy extends LitElement {
|
||||
min-height: 100vh;
|
||||
box-sizing: border-box;
|
||||
padding-left: env(safe-area-inset-left);
|
||||
padding-inline-start: env(safe-area-inset-left);
|
||||
padding-right: env(safe-area-inset-right);
|
||||
padding-inline-start: env(safe-area-inset-left);
|
||||
padding-inline-end: env(safe-area-inset-right);
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
}
|
||||
hui-view {
|
||||
background: var(
|
||||
--lovelace-background,
|
||||
var(--primary-background-color)
|
||||
|
7
src/panels/lovelace/card-features/common/filter-modes.ts
Normal file
7
src/panels/lovelace/card-features/common/filter-modes.ts
Normal file
@ -0,0 +1,7 @@
|
||||
export const filterModes = (
|
||||
supportedModes: string[] | undefined,
|
||||
selectedModes: string[] | undefined
|
||||
): string[] =>
|
||||
selectedModes
|
||||
? selectedModes.filter((mode) => (supportedModes || []).includes(mode))
|
||||
: supportedModes || [];
|
@ -15,6 +15,7 @@ import { UNAVAILABLE } from "../../../data/entity";
|
||||
import { HomeAssistant } from "../../../types";
|
||||
import { LovelaceCardFeature, LovelaceCardFeatureEditor } from "../types";
|
||||
import { ClimateFanModesCardFeatureConfig } from "./types";
|
||||
import { filterModes } from "./common/filter-modes";
|
||||
|
||||
export const supportsClimateFanModesCardFeature = (stateObj: HassEntity) => {
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
@ -40,14 +41,10 @@ class HuiClimateFanModesCardFeature
|
||||
@query("ha-control-select-menu", true)
|
||||
private _haSelect?: HaControlSelectMenu;
|
||||
|
||||
static getStubConfig(
|
||||
_,
|
||||
stateObj?: HassEntity
|
||||
): ClimateFanModesCardFeatureConfig {
|
||||
static getStubConfig(): ClimateFanModesCardFeatureConfig {
|
||||
return {
|
||||
type: "climate-fan-modes",
|
||||
style: "dropdown",
|
||||
fan_modes: stateObj?.attributes.fan_modes || [],
|
||||
};
|
||||
}
|
||||
|
||||
@ -122,25 +119,24 @@ class HuiClimateFanModesCardFeature
|
||||
|
||||
const stateObj = this.stateObj;
|
||||
|
||||
const modes = stateObj.attributes.fan_modes || [];
|
||||
|
||||
const options = modes
|
||||
.filter((mode) => (this._config!.fan_modes || []).includes(mode))
|
||||
.map<ControlSelectOption>((mode) => ({
|
||||
value: mode,
|
||||
label: this.hass!.formatEntityAttributeValue(
|
||||
this.stateObj!,
|
||||
"fan_mode",
|
||||
mode
|
||||
),
|
||||
icon: html`<ha-attribute-icon
|
||||
slot="graphic"
|
||||
.hass=${this.hass}
|
||||
.stateObj=${stateObj}
|
||||
attribute="fan_mode"
|
||||
.attributeValue=${mode}
|
||||
></ha-attribute-icon>`,
|
||||
}));
|
||||
const options = filterModes(
|
||||
stateObj.attributes.fan_modes,
|
||||
this._config!.fan_modes
|
||||
).map<ControlSelectOption>((mode) => ({
|
||||
value: mode,
|
||||
label: this.hass!.formatEntityAttributeValue(
|
||||
this.stateObj!,
|
||||
"fan_mode",
|
||||
mode
|
||||
),
|
||||
icon: html`<ha-attribute-icon
|
||||
slot="graphic"
|
||||
.hass=${this.hass}
|
||||
.stateObj=${stateObj}
|
||||
attribute="fan_mode"
|
||||
.attributeValue=${mode}
|
||||
></ha-attribute-icon>`,
|
||||
}));
|
||||
|
||||
if (this._config.style === "icons") {
|
||||
return html`
|
||||
|
@ -19,6 +19,7 @@ import {
|
||||
import { UNAVAILABLE } from "../../../data/entity";
|
||||
import { HomeAssistant } from "../../../types";
|
||||
import { LovelaceCardFeature, LovelaceCardFeatureEditor } from "../types";
|
||||
import { filterModes } from "./common/filter-modes";
|
||||
import { ClimateHvacModesCardFeatureConfig } from "./types";
|
||||
|
||||
export const supportsClimateHvacModesCardFeature = (stateObj: HassEntity) => {
|
||||
@ -42,13 +43,9 @@ class HuiClimateHvacModesCardFeature
|
||||
@query("ha-control-select-menu", true)
|
||||
private _haSelect?: HaControlSelectMenu;
|
||||
|
||||
static getStubConfig(
|
||||
_,
|
||||
stateObj?: HassEntity
|
||||
): ClimateHvacModesCardFeatureConfig {
|
||||
static getStubConfig(): ClimateHvacModesCardFeatureConfig {
|
||||
return {
|
||||
type: "climate-hvac-modes",
|
||||
hvac_modes: stateObj?.attributes.hvac_modes || [],
|
||||
};
|
||||
}
|
||||
|
||||
@ -122,21 +119,23 @@ class HuiClimateHvacModesCardFeature
|
||||
|
||||
const color = stateColorCss(this.stateObj);
|
||||
|
||||
const modes = this._config.hvac_modes || [];
|
||||
const ordererHvacModes = (this.stateObj.attributes.hvac_modes || [])
|
||||
.concat()
|
||||
.sort(compareClimateHvacModes);
|
||||
|
||||
const options = modes
|
||||
.filter((mode) => this.stateObj?.attributes.hvac_modes.includes(mode))
|
||||
.sort(compareClimateHvacModes)
|
||||
.map<ControlSelectOption>((mode) => ({
|
||||
value: mode,
|
||||
label: this.hass!.formatEntityState(this.stateObj!, mode),
|
||||
icon: html`
|
||||
<ha-svg-icon
|
||||
slot="graphic"
|
||||
.path=${climateHvacModeIcon(mode)}
|
||||
></ha-svg-icon>
|
||||
`,
|
||||
}));
|
||||
const options = filterModes(
|
||||
ordererHvacModes,
|
||||
this._config.hvac_modes
|
||||
).map<ControlSelectOption>((mode) => ({
|
||||
value: mode,
|
||||
label: this.hass!.formatEntityState(this.stateObj!, mode),
|
||||
icon: html`
|
||||
<ha-svg-icon
|
||||
slot="graphic"
|
||||
.path=${climateHvacModeIcon(mode)}
|
||||
></ha-svg-icon>
|
||||
`,
|
||||
}));
|
||||
|
||||
if (this._config.style === "dropdown") {
|
||||
return html`
|
||||
|
@ -15,6 +15,7 @@ import { UNAVAILABLE } from "../../../data/entity";
|
||||
import { HomeAssistant } from "../../../types";
|
||||
import { LovelaceCardFeature, LovelaceCardFeatureEditor } from "../types";
|
||||
import { ClimatePresetModesCardFeatureConfig } from "./types";
|
||||
import { filterModes } from "./common/filter-modes";
|
||||
|
||||
export const supportsClimatePresetModesCardFeature = (stateObj: HassEntity) => {
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
@ -40,14 +41,10 @@ class HuiClimatePresetModesCardFeature
|
||||
@query("ha-control-select-menu", true)
|
||||
private _haSelect?: HaControlSelectMenu;
|
||||
|
||||
static getStubConfig(
|
||||
_,
|
||||
stateObj?: HassEntity
|
||||
): ClimatePresetModesCardFeatureConfig {
|
||||
static getStubConfig(): ClimatePresetModesCardFeatureConfig {
|
||||
return {
|
||||
type: "climate-preset-modes",
|
||||
style: "dropdown",
|
||||
preset_modes: stateObj?.attributes.preset_modes || [],
|
||||
};
|
||||
}
|
||||
|
||||
@ -124,25 +121,24 @@ class HuiClimatePresetModesCardFeature
|
||||
|
||||
const stateObj = this.stateObj;
|
||||
|
||||
const modes = stateObj.attributes.preset_modes || [];
|
||||
|
||||
const options = modes
|
||||
.filter((mode) => (this._config!.preset_modes || []).includes(mode))
|
||||
.map<ControlSelectOption>((mode) => ({
|
||||
value: mode,
|
||||
label: this.hass!.formatEntityAttributeValue(
|
||||
this.stateObj!,
|
||||
"preset_mode",
|
||||
mode
|
||||
),
|
||||
icon: html`<ha-attribute-icon
|
||||
slot="graphic"
|
||||
.hass=${this.hass}
|
||||
.stateObj=${stateObj}
|
||||
attribute="preset_mode"
|
||||
.attributeValue=${mode}
|
||||
></ha-attribute-icon>`,
|
||||
}));
|
||||
const options = filterModes(
|
||||
stateObj.attributes.preset_modes,
|
||||
this._config!.preset_modes
|
||||
).map<ControlSelectOption>((mode) => ({
|
||||
value: mode,
|
||||
label: this.hass!.formatEntityAttributeValue(
|
||||
this.stateObj!,
|
||||
"preset_mode",
|
||||
mode
|
||||
),
|
||||
icon: html`<ha-attribute-icon
|
||||
slot="graphic"
|
||||
.hass=${this.hass}
|
||||
.stateObj=${stateObj}
|
||||
attribute="preset_mode"
|
||||
.attributeValue=${mode}
|
||||
></ha-attribute-icon>`,
|
||||
}));
|
||||
|
||||
if (this._config.style === "icons") {
|
||||
return html`
|
||||
|
@ -15,6 +15,7 @@ import { UNAVAILABLE } from "../../../data/entity";
|
||||
import { HomeAssistant } from "../../../types";
|
||||
import { LovelaceCardFeature, LovelaceCardFeatureEditor } from "../types";
|
||||
import { ClimateSwingModesCardFeatureConfig } from "./types";
|
||||
import { filterModes } from "./common/filter-modes";
|
||||
|
||||
export const supportsClimateSwingModesCardFeature = (stateObj: HassEntity) => {
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
@ -40,14 +41,10 @@ class HuiClimateSwingModesCardFeature
|
||||
@query("ha-control-select-menu", true)
|
||||
private _haSelect?: HaControlSelectMenu;
|
||||
|
||||
static getStubConfig(
|
||||
_,
|
||||
stateObj?: HassEntity
|
||||
): ClimateSwingModesCardFeatureConfig {
|
||||
static getStubConfig(): ClimateSwingModesCardFeatureConfig {
|
||||
return {
|
||||
type: "climate-swing-modes",
|
||||
style: "dropdown",
|
||||
swing_modes: stateObj?.attributes.swing_modes || [],
|
||||
};
|
||||
}
|
||||
|
||||
@ -124,25 +121,24 @@ class HuiClimateSwingModesCardFeature
|
||||
|
||||
const stateObj = this.stateObj;
|
||||
|
||||
const modes = stateObj.attributes.swing_modes || [];
|
||||
|
||||
const options = modes
|
||||
.filter((mode) => (this._config!.swing_modes || []).includes(mode))
|
||||
.map<ControlSelectOption>((mode) => ({
|
||||
value: mode,
|
||||
label: this.hass!.formatEntityAttributeValue(
|
||||
this.stateObj!,
|
||||
"swing_mode",
|
||||
mode
|
||||
),
|
||||
icon: html`<ha-attribute-icon
|
||||
slot="graphic"
|
||||
.hass=${this.hass}
|
||||
.stateObj=${stateObj}
|
||||
attribute="swing_mode"
|
||||
.attributeValue=${mode}
|
||||
></ha-attribute-icon>`,
|
||||
}));
|
||||
const options = filterModes(
|
||||
stateObj.attributes.swing_modes,
|
||||
this._config!.swing_modes
|
||||
).map<ControlSelectOption>((mode) => ({
|
||||
value: mode,
|
||||
label: this.hass!.formatEntityAttributeValue(
|
||||
this.stateObj!,
|
||||
"swing_mode",
|
||||
mode
|
||||
),
|
||||
icon: html`<ha-attribute-icon
|
||||
slot="graphic"
|
||||
.hass=${this.hass}
|
||||
.stateObj=${stateObj}
|
||||
attribute="swing_mode"
|
||||
.attributeValue=${mode}
|
||||
></ha-attribute-icon>`,
|
||||
}));
|
||||
|
||||
if (this._config.style === "icons") {
|
||||
return html`
|
||||
|
@ -15,6 +15,7 @@ import { UNAVAILABLE } from "../../../data/entity";
|
||||
import { HomeAssistant } from "../../../types";
|
||||
import { LovelaceCardFeature, LovelaceCardFeatureEditor } from "../types";
|
||||
import { FanPresetModesCardFeatureConfig } from "./types";
|
||||
import { filterModes } from "./common/filter-modes";
|
||||
|
||||
export const supportsFanPresetModesCardFeature = (stateObj: HassEntity) => {
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
@ -39,14 +40,10 @@ class HuiFanPresetModesCardFeature
|
||||
@query("ha-control-select-menu", true)
|
||||
private _haSelect?: HaControlSelectMenu;
|
||||
|
||||
static getStubConfig(
|
||||
_,
|
||||
stateObj?: HassEntity
|
||||
): FanPresetModesCardFeatureConfig {
|
||||
static getStubConfig(): FanPresetModesCardFeatureConfig {
|
||||
return {
|
||||
type: "fan-preset-modes",
|
||||
style: "dropdown",
|
||||
preset_modes: stateObj?.attributes.preset_modes || [],
|
||||
};
|
||||
}
|
||||
|
||||
@ -121,25 +118,24 @@ class HuiFanPresetModesCardFeature
|
||||
|
||||
const stateObj = this.stateObj;
|
||||
|
||||
const modes = stateObj.attributes.preset_modes || [];
|
||||
|
||||
const options = modes
|
||||
.filter((mode) => (this._config!.preset_modes || []).includes(mode))
|
||||
.map<ControlSelectOption>((mode) => ({
|
||||
value: mode,
|
||||
label: this.hass!.formatEntityAttributeValue(
|
||||
this.stateObj!,
|
||||
"preset_mode",
|
||||
mode
|
||||
),
|
||||
icon: html`<ha-attribute-icon
|
||||
slot="graphic"
|
||||
.hass=${this.hass}
|
||||
.stateObj=${stateObj}
|
||||
attribute="preset_mode"
|
||||
.attributeValue=${mode}
|
||||
></ha-attribute-icon>`,
|
||||
}));
|
||||
const options = filterModes(
|
||||
stateObj.attributes.preset_modes,
|
||||
this._config!.preset_modes
|
||||
).map<ControlSelectOption>((mode) => ({
|
||||
value: mode,
|
||||
label: this.hass!.formatEntityAttributeValue(
|
||||
this.stateObj!,
|
||||
"preset_mode",
|
||||
mode
|
||||
),
|
||||
icon: html`<ha-attribute-icon
|
||||
slot="graphic"
|
||||
.hass=${this.hass}
|
||||
.stateObj=${stateObj}
|
||||
attribute="preset_mode"
|
||||
.attributeValue=${mode}
|
||||
></ha-attribute-icon>`,
|
||||
}));
|
||||
|
||||
if (this._config.style === "icons") {
|
||||
return html`
|
||||
|
@ -18,6 +18,7 @@ import {
|
||||
import { HomeAssistant } from "../../../types";
|
||||
import { LovelaceCardFeature, LovelaceCardFeatureEditor } from "../types";
|
||||
import { HumidifierModesCardFeatureConfig } from "./types";
|
||||
import { filterModes } from "./common/filter-modes";
|
||||
|
||||
export const supportsHumidifierModesCardFeature = (stateObj: HassEntity) => {
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
@ -43,14 +44,10 @@ class HuiHumidifierModesCardFeature
|
||||
@query("ha-control-select-menu", true)
|
||||
private _haSelect?: HaControlSelectMenu;
|
||||
|
||||
static getStubConfig(
|
||||
_,
|
||||
stateObj?: HassEntity
|
||||
): HumidifierModesCardFeatureConfig {
|
||||
static getStubConfig(): HumidifierModesCardFeatureConfig {
|
||||
return {
|
||||
type: "humidifier-modes",
|
||||
style: "dropdown",
|
||||
modes: stateObj?.attributes.available_modes || [],
|
||||
};
|
||||
}
|
||||
|
||||
@ -125,25 +122,24 @@ class HuiHumidifierModesCardFeature
|
||||
|
||||
const stateObj = this.stateObj;
|
||||
|
||||
const modes = stateObj.attributes.available_modes || [];
|
||||
|
||||
const options = modes
|
||||
.filter((mode) => (this._config!.modes || []).includes(mode))
|
||||
.map<ControlSelectOption>((mode) => ({
|
||||
value: mode,
|
||||
label: this.hass!.formatEntityAttributeValue(
|
||||
this.stateObj!,
|
||||
"mode",
|
||||
mode
|
||||
),
|
||||
icon: html`<ha-attribute-icon
|
||||
slot="graphic"
|
||||
.hass=${this.hass}
|
||||
.stateObj=${stateObj}
|
||||
attribute="mode"
|
||||
.attributeValue=${mode}
|
||||
></ha-attribute-icon>`,
|
||||
}));
|
||||
const options = filterModes(
|
||||
stateObj.attributes.available_modes,
|
||||
this._config!.modes
|
||||
).map<ControlSelectOption>((mode) => ({
|
||||
value: mode,
|
||||
label: this.hass!.formatEntityAttributeValue(
|
||||
this.stateObj!,
|
||||
"mode",
|
||||
mode
|
||||
),
|
||||
icon: html`<ha-attribute-icon
|
||||
slot="graphic"
|
||||
.hass=${this.hass}
|
||||
.stateObj=${stateObj}
|
||||
attribute="mode"
|
||||
.attributeValue=${mode}
|
||||
></ha-attribute-icon>`,
|
||||
}));
|
||||
|
||||
if (this._config.style === "icons") {
|
||||
return html`
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { mdiLock, mdiLockOpen } from "@mdi/js";
|
||||
import { mdiLock, mdiLockOpenVariant } from "@mdi/js";
|
||||
import { HassEntity } from "home-assistant-js-websocket";
|
||||
import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
@ -90,7 +90,7 @@ class HuiLockCommandsCardFeature
|
||||
pulse: isLocking(this.stateObj) || isUnlocking(this.stateObj),
|
||||
})}
|
||||
>
|
||||
<ha-svg-icon .path=${mdiLockOpen}></ha-svg-icon>
|
||||
<ha-svg-icon .path=${mdiLockOpenVariant}></ha-svg-icon>
|
||||
</ha-control-button>
|
||||
</ha-control-button-group>
|
||||
`;
|
||||
|
@ -9,8 +9,9 @@ import { UNAVAILABLE } from "../../../data/entity";
|
||||
import { InputSelectEntity } from "../../../data/input_select";
|
||||
import { SelectEntity } from "../../../data/select";
|
||||
import { HomeAssistant } from "../../../types";
|
||||
import { LovelaceCardFeature } from "../types";
|
||||
import { LovelaceCardFeature, LovelaceCardFeatureEditor } from "../types";
|
||||
import { SelectOptionsCardFeatureConfig } from "./types";
|
||||
import { filterModes } from "./common/filter-modes";
|
||||
|
||||
export const supportsSelectOptionsCardFeature = (stateObj: HassEntity) => {
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
@ -41,6 +42,13 @@ class HuiSelectOptionsCardFeature
|
||||
};
|
||||
}
|
||||
|
||||
public static async getConfigElement(): Promise<LovelaceCardFeatureEditor> {
|
||||
await import(
|
||||
"../editor/config-elements/hui-select-options-card-feature-editor"
|
||||
);
|
||||
return document.createElement("hui-select-options-card-feature-editor");
|
||||
}
|
||||
|
||||
public setConfig(config: SelectOptionsCardFeatureConfig): void {
|
||||
if (!config) {
|
||||
throw new Error("Invalid configuration");
|
||||
@ -105,6 +113,11 @@ class HuiSelectOptionsCardFeature
|
||||
|
||||
const stateObj = this.stateObj;
|
||||
|
||||
const options = filterModes(
|
||||
this.stateObj.attributes.options,
|
||||
this._config.options
|
||||
);
|
||||
|
||||
return html`
|
||||
<div class="container">
|
||||
<ha-control-select-menu
|
||||
@ -118,7 +131,7 @@ class HuiSelectOptionsCardFeature
|
||||
@selected=${this._valueChanged}
|
||||
@closed=${stopPropagation}
|
||||
>
|
||||
${stateObj.attributes.options!.map(
|
||||
${options.map(
|
||||
(option) => html`
|
||||
<ha-list-item .value=${option}>
|
||||
${this.hass!.formatEntityState(stateObj, option)}
|
||||
|
@ -19,6 +19,7 @@ import {
|
||||
import { HomeAssistant } from "../../../types";
|
||||
import { LovelaceCardFeature, LovelaceCardFeatureEditor } from "../types";
|
||||
import { WaterHeaterOperationModesCardFeatureConfig } from "./types";
|
||||
import { filterModes } from "./common/filter-modes";
|
||||
|
||||
export const supportsWaterHeaterOperationModesCardFeature = (
|
||||
stateObj: HassEntity
|
||||
@ -40,13 +41,9 @@ class HuiWaterHeaterOperationModeCardFeature
|
||||
|
||||
@state() _currentOperationMode?: OperationMode;
|
||||
|
||||
static getStubConfig(
|
||||
_,
|
||||
stateObj?: HassEntity
|
||||
): WaterHeaterOperationModesCardFeatureConfig {
|
||||
static getStubConfig(): WaterHeaterOperationModesCardFeatureConfig {
|
||||
return {
|
||||
type: "water-heater-operation-modes",
|
||||
operation_modes: stateObj?.attributes.operation_list || [],
|
||||
};
|
||||
}
|
||||
|
||||
@ -107,16 +104,18 @@ class HuiWaterHeaterOperationModeCardFeature
|
||||
|
||||
const color = stateColorCss(this.stateObj);
|
||||
|
||||
const modes = this._config.operation_modes || [];
|
||||
const orderedModes = (this.stateObj.attributes.operation_list || [])
|
||||
.concat()
|
||||
.sort(compareWaterHeaterOperationMode);
|
||||
|
||||
const options = modes
|
||||
.filter((mode) => this.stateObj?.attributes.operation_list.includes(mode))
|
||||
.sort(compareWaterHeaterOperationMode)
|
||||
.map<ControlSelectOption>((mode) => ({
|
||||
value: mode,
|
||||
label: this.hass!.formatEntityState(this.stateObj!, mode),
|
||||
path: computeOperationModeIcon(mode),
|
||||
}));
|
||||
const options = filterModes(
|
||||
orderedModes,
|
||||
this._config.operation_modes
|
||||
).map<ControlSelectOption>((mode) => ({
|
||||
value: mode,
|
||||
label: this.hass!.formatEntityState(this.stateObj!, mode),
|
||||
path: computeOperationModeIcon(mode as OperationMode),
|
||||
}));
|
||||
|
||||
return html`
|
||||
<div class="container">
|
||||
|
@ -75,6 +75,7 @@ export interface ClimatePresetModesCardFeatureConfig {
|
||||
|
||||
export interface SelectOptionsCardFeatureConfig {
|
||||
type: "select-options";
|
||||
options?: string[];
|
||||
}
|
||||
|
||||
export interface NumericInputCardFeatureConfig {
|
||||
|
@ -21,9 +21,9 @@ import {
|
||||
import { HomeAssistant } from "../../../../types";
|
||||
import { supportsAlarmModesCardFeature } from "../../card-features/hui-alarm-modes-card-feature";
|
||||
import { supportsClimateFanModesCardFeature } from "../../card-features/hui-climate-fan-modes-card-feature";
|
||||
import { supportsClimateSwingModesCardFeature } from "../../card-features/hui-climate-swing-modes-card-feature";
|
||||
import { supportsClimateHvacModesCardFeature } from "../../card-features/hui-climate-hvac-modes-card-feature";
|
||||
import { supportsClimatePresetModesCardFeature } from "../../card-features/hui-climate-preset-modes-card-feature";
|
||||
import { supportsClimateSwingModesCardFeature } from "../../card-features/hui-climate-swing-modes-card-feature";
|
||||
import { supportsCoverOpenCloseCardFeature } from "../../card-features/hui-cover-open-close-card-feature";
|
||||
import { supportsCoverPositionCardFeature } from "../../card-features/hui-cover-position-card-feature";
|
||||
import { supportsCoverTiltCardFeature } from "../../card-features/hui-cover-tilt-card-feature";
|
||||
@ -53,13 +53,13 @@ type SupportsFeature = (stateObj: HassEntity) => boolean;
|
||||
const UI_FEATURE_TYPES = [
|
||||
"alarm-modes",
|
||||
"climate-fan-modes",
|
||||
"climate-swing-modes",
|
||||
"climate-hvac-modes",
|
||||
"climate-preset-modes",
|
||||
"climate-swing-modes",
|
||||
"cover-open-close",
|
||||
"cover-position",
|
||||
"cover-tilt",
|
||||
"cover-tilt-position",
|
||||
"cover-tilt",
|
||||
"fan-preset-modes",
|
||||
"fan-speed",
|
||||
"humidifier-modes",
|
||||
@ -82,14 +82,15 @@ type UiFeatureTypes = (typeof UI_FEATURE_TYPES)[number];
|
||||
|
||||
const EDITABLES_FEATURE_TYPES = new Set<UiFeatureTypes>([
|
||||
"alarm-modes",
|
||||
"climate-hvac-modes",
|
||||
"climate-fan-modes",
|
||||
"climate-swing-modes",
|
||||
"climate-hvac-modes",
|
||||
"climate-preset-modes",
|
||||
"climate-swing-modes",
|
||||
"fan-preset-modes",
|
||||
"humidifier-modes",
|
||||
"lawn-mower-commands",
|
||||
"numeric-input",
|
||||
"select-options",
|
||||
"update-actions",
|
||||
"vacuum-commands",
|
||||
"water-heater-operation-modes",
|
||||
|
@ -17,6 +17,10 @@ import {
|
||||
} from "../../card-features/types";
|
||||
import type { LovelaceCardFeatureEditor } from "../../types";
|
||||
|
||||
type ClimateFanModesCardFeatureData = ClimateFanModesCardFeatureConfig & {
|
||||
customize_modes: boolean;
|
||||
};
|
||||
|
||||
@customElement("hui-climate-fan-modes-card-feature-editor")
|
||||
export class HuiClimateFanModesCardFeatureEditor
|
||||
extends LitElement
|
||||
@ -36,7 +40,8 @@ export class HuiClimateFanModesCardFeatureEditor
|
||||
(
|
||||
localize: LocalizeFunc,
|
||||
formatEntityAttributeValue: FormatEntityAttributeValueFunc,
|
||||
stateObj?: HassEntity
|
||||
stateObj: HassEntity | undefined,
|
||||
customizeModes: boolean
|
||||
) =>
|
||||
[
|
||||
{
|
||||
@ -55,19 +60,33 @@ export class HuiClimateFanModesCardFeatureEditor
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "fan_modes",
|
||||
name: "customize_modes",
|
||||
selector: {
|
||||
select: {
|
||||
multiple: true,
|
||||
mode: "list",
|
||||
options:
|
||||
stateObj?.attributes.fan_modes?.map((mode) => ({
|
||||
value: mode,
|
||||
label: formatEntityAttributeValue(stateObj, "fan_mode", mode),
|
||||
})) || [],
|
||||
},
|
||||
boolean: {},
|
||||
},
|
||||
},
|
||||
...(customizeModes
|
||||
? ([
|
||||
{
|
||||
name: "fan_modes",
|
||||
selector: {
|
||||
select: {
|
||||
multiple: true,
|
||||
reorder: true,
|
||||
options:
|
||||
stateObj?.attributes.fan_modes?.map((mode) => ({
|
||||
value: mode,
|
||||
label: formatEntityAttributeValue(
|
||||
stateObj,
|
||||
"fan_mode",
|
||||
mode
|
||||
),
|
||||
})) || [],
|
||||
},
|
||||
},
|
||||
},
|
||||
] as const satisfies readonly HaFormSchema[])
|
||||
: []),
|
||||
] as const satisfies readonly HaFormSchema[]
|
||||
);
|
||||
|
||||
@ -80,16 +99,17 @@ export class HuiClimateFanModesCardFeatureEditor
|
||||
? this.hass.states[this.context?.entity_id]
|
||||
: undefined;
|
||||
|
||||
const data: ClimateFanModesCardFeatureConfig = {
|
||||
const data: ClimateFanModesCardFeatureData = {
|
||||
style: "dropdown",
|
||||
fan_modes: [],
|
||||
...this._config,
|
||||
customize_modes: this._config.fan_modes !== undefined,
|
||||
};
|
||||
|
||||
const schema = this._schema(
|
||||
this.hass.localize,
|
||||
this.hass.formatEntityAttributeValue,
|
||||
stateObj
|
||||
stateObj,
|
||||
data.customize_modes
|
||||
);
|
||||
|
||||
return html`
|
||||
@ -104,7 +124,21 @@ export class HuiClimateFanModesCardFeatureEditor
|
||||
}
|
||||
|
||||
private _valueChanged(ev: CustomEvent): void {
|
||||
fireEvent(this, "config-changed", { config: ev.detail.value });
|
||||
const { customize_modes, ...config } = ev.detail
|
||||
.value as ClimateFanModesCardFeatureData;
|
||||
|
||||
const stateObj = this.context?.entity_id
|
||||
? this.hass!.states[this.context?.entity_id]
|
||||
: undefined;
|
||||
|
||||
if (customize_modes && !config.fan_modes) {
|
||||
config.fan_modes = stateObj?.attributes.fan_modes || [];
|
||||
}
|
||||
if (!customize_modes && config.fan_modes) {
|
||||
delete config.fan_modes;
|
||||
}
|
||||
|
||||
fireEvent(this, "config-changed", { config: config });
|
||||
}
|
||||
|
||||
private _computeLabelCallback = (
|
||||
@ -113,6 +147,7 @@ export class HuiClimateFanModesCardFeatureEditor
|
||||
switch (schema.name) {
|
||||
case "style":
|
||||
case "fan_modes":
|
||||
case "customize_modes":
|
||||
return this.hass!.localize(
|
||||
`ui.panel.lovelace.editor.features.types.climate-fan-modes.${schema.name}`
|
||||
);
|
||||
|
@ -6,8 +6,11 @@ import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import type { FormatEntityStateFunc } from "../../../../common/translations/entity-state";
|
||||
import type { LocalizeFunc } from "../../../../common/translations/localize";
|
||||
import "../../../../components/ha-form/ha-form";
|
||||
import type { SchemaUnion } from "../../../../components/ha-form/types";
|
||||
import { HVAC_MODES } from "../../../../data/climate";
|
||||
import type {
|
||||
HaFormSchema,
|
||||
SchemaUnion,
|
||||
} from "../../../../components/ha-form/types";
|
||||
import { compareClimateHvacModes } from "../../../../data/climate";
|
||||
import type { HomeAssistant } from "../../../../types";
|
||||
import {
|
||||
ClimateHvacModesCardFeatureConfig,
|
||||
@ -15,6 +18,10 @@ import {
|
||||
} from "../../card-features/types";
|
||||
import type { LovelaceCardFeatureEditor } from "../../types";
|
||||
|
||||
type ClimateHvacModesCardFeatureData = ClimateHvacModesCardFeatureConfig & {
|
||||
customize_modes: boolean;
|
||||
};
|
||||
|
||||
@customElement("hui-climate-hvac-modes-card-feature-editor")
|
||||
export class HuiClimateHvacModesCardFeatureEditor
|
||||
extends LitElement
|
||||
@ -34,7 +41,8 @@ export class HuiClimateHvacModesCardFeatureEditor
|
||||
(
|
||||
localize: LocalizeFunc,
|
||||
formatEntityState: FormatEntityStateFunc,
|
||||
stateObj?: HassEntity
|
||||
stateObj: HassEntity | undefined,
|
||||
customizeModes: boolean
|
||||
) =>
|
||||
[
|
||||
{
|
||||
@ -53,21 +61,34 @@ export class HuiClimateHvacModesCardFeatureEditor
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "hvac_modes",
|
||||
name: "customize_modes",
|
||||
selector: {
|
||||
select: {
|
||||
multiple: true,
|
||||
mode: "list",
|
||||
options: HVAC_MODES.filter((mode) =>
|
||||
stateObj?.attributes.hvac_modes?.includes(mode)
|
||||
).map((mode) => ({
|
||||
value: mode,
|
||||
label: stateObj ? formatEntityState(stateObj, mode) : mode,
|
||||
})),
|
||||
},
|
||||
boolean: {},
|
||||
},
|
||||
},
|
||||
] as const
|
||||
...(customizeModes
|
||||
? ([
|
||||
{
|
||||
name: "hvac_modes",
|
||||
selector: {
|
||||
select: {
|
||||
reorder: true,
|
||||
multiple: true,
|
||||
options: (stateObj?.attributes.hvac_modes || [])
|
||||
.concat()
|
||||
.sort(compareClimateHvacModes)
|
||||
.map((mode) => ({
|
||||
value: mode,
|
||||
label: stateObj
|
||||
? formatEntityState(stateObj, mode)
|
||||
: mode,
|
||||
})),
|
||||
},
|
||||
},
|
||||
},
|
||||
] as const satisfies readonly HaFormSchema[])
|
||||
: []),
|
||||
] as const satisfies readonly HaFormSchema[]
|
||||
);
|
||||
|
||||
protected render() {
|
||||
@ -79,16 +100,17 @@ export class HuiClimateHvacModesCardFeatureEditor
|
||||
? this.hass.states[this.context?.entity_id]
|
||||
: undefined;
|
||||
|
||||
const data: ClimateHvacModesCardFeatureConfig = {
|
||||
const data: ClimateHvacModesCardFeatureData = {
|
||||
style: "icons",
|
||||
hvac_modes: [],
|
||||
...this._config,
|
||||
customize_modes: this._config.hvac_modes !== undefined,
|
||||
};
|
||||
|
||||
const schema = this._schema(
|
||||
this.hass.localize,
|
||||
this.hass.formatEntityState,
|
||||
stateObj
|
||||
stateObj,
|
||||
data.customize_modes
|
||||
);
|
||||
|
||||
return html`
|
||||
@ -103,7 +125,24 @@ export class HuiClimateHvacModesCardFeatureEditor
|
||||
}
|
||||
|
||||
private _valueChanged(ev: CustomEvent): void {
|
||||
fireEvent(this, "config-changed", { config: ev.detail.value });
|
||||
const { customize_modes, ...config } = ev.detail
|
||||
.value as ClimateHvacModesCardFeatureData;
|
||||
|
||||
const stateObj = this.context?.entity_id
|
||||
? this.hass!.states[this.context?.entity_id]
|
||||
: undefined;
|
||||
|
||||
if (customize_modes && !config.hvac_modes) {
|
||||
const ordererHvacModes = (stateObj?.attributes.hvac_modes || [])
|
||||
.concat()
|
||||
.sort(compareClimateHvacModes);
|
||||
config.hvac_modes = ordererHvacModes;
|
||||
}
|
||||
if (!customize_modes && config.hvac_modes) {
|
||||
delete config.hvac_modes;
|
||||
}
|
||||
|
||||
fireEvent(this, "config-changed", { config: config });
|
||||
}
|
||||
|
||||
private _computeLabelCallback = (
|
||||
@ -112,6 +151,7 @@ export class HuiClimateHvacModesCardFeatureEditor
|
||||
switch (schema.name) {
|
||||
case "hvac_modes":
|
||||
case "style":
|
||||
case "customize_modes":
|
||||
return this.hass!.localize(
|
||||
`ui.panel.lovelace.editor.features.types.climate-hvac-modes.${schema.name}`
|
||||
);
|
||||
|
@ -17,6 +17,10 @@ import {
|
||||
} from "../../card-features/types";
|
||||
import type { LovelaceCardFeatureEditor } from "../../types";
|
||||
|
||||
type ClimatePresetModesCardFeatureData = ClimatePresetModesCardFeatureConfig & {
|
||||
customize_modes: boolean;
|
||||
};
|
||||
|
||||
@customElement("hui-climate-preset-modes-card-feature-editor")
|
||||
export class HuiClimatePresetModesCardFeatureEditor
|
||||
extends LitElement
|
||||
@ -36,7 +40,8 @@ export class HuiClimatePresetModesCardFeatureEditor
|
||||
(
|
||||
localize: LocalizeFunc,
|
||||
formatEntityAttributeValue: FormatEntityAttributeValueFunc,
|
||||
stateObj?: HassEntity
|
||||
stateObj: HassEntity | undefined,
|
||||
customizeModes: boolean
|
||||
) =>
|
||||
[
|
||||
{
|
||||
@ -55,23 +60,33 @@ export class HuiClimatePresetModesCardFeatureEditor
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "preset_modes",
|
||||
name: "customize_modes",
|
||||
selector: {
|
||||
select: {
|
||||
multiple: true,
|
||||
mode: "list",
|
||||
options:
|
||||
stateObj?.attributes.preset_modes?.map((mode) => ({
|
||||
value: mode,
|
||||
label: formatEntityAttributeValue(
|
||||
stateObj,
|
||||
"preset_mode",
|
||||
mode
|
||||
),
|
||||
})) || [],
|
||||
},
|
||||
boolean: {},
|
||||
},
|
||||
},
|
||||
...(customizeModes
|
||||
? ([
|
||||
{
|
||||
name: "preset_modes",
|
||||
selector: {
|
||||
select: {
|
||||
reorder: true,
|
||||
multiple: true,
|
||||
options:
|
||||
stateObj?.attributes.preset_modes?.map((mode) => ({
|
||||
value: mode,
|
||||
label: formatEntityAttributeValue(
|
||||
stateObj,
|
||||
"preset_mode",
|
||||
mode
|
||||
),
|
||||
})) || [],
|
||||
},
|
||||
},
|
||||
},
|
||||
] as const satisfies readonly HaFormSchema[])
|
||||
: []),
|
||||
] as const satisfies readonly HaFormSchema[]
|
||||
);
|
||||
|
||||
@ -84,16 +99,17 @@ export class HuiClimatePresetModesCardFeatureEditor
|
||||
? this.hass.states[this.context?.entity_id]
|
||||
: undefined;
|
||||
|
||||
const data: ClimatePresetModesCardFeatureConfig = {
|
||||
const data: ClimatePresetModesCardFeatureData = {
|
||||
style: "dropdown",
|
||||
preset_modes: [],
|
||||
...this._config,
|
||||
customize_modes: this._config.preset_modes !== undefined,
|
||||
};
|
||||
|
||||
const schema = this._schema(
|
||||
this.hass.localize,
|
||||
this.hass.formatEntityAttributeValue,
|
||||
stateObj
|
||||
stateObj,
|
||||
data.customize_modes
|
||||
);
|
||||
|
||||
return html`
|
||||
@ -108,7 +124,21 @@ export class HuiClimatePresetModesCardFeatureEditor
|
||||
}
|
||||
|
||||
private _valueChanged(ev: CustomEvent): void {
|
||||
fireEvent(this, "config-changed", { config: ev.detail.value });
|
||||
const { customize_modes, ...config } = ev.detail
|
||||
.value as ClimatePresetModesCardFeatureData;
|
||||
|
||||
const stateObj = this.context?.entity_id
|
||||
? this.hass!.states[this.context?.entity_id]
|
||||
: undefined;
|
||||
|
||||
if (customize_modes && !config.preset_modes) {
|
||||
config.preset_modes = stateObj?.attributes.preset_modes || [];
|
||||
}
|
||||
if (!customize_modes && config.preset_modes) {
|
||||
delete config.preset_modes;
|
||||
}
|
||||
|
||||
fireEvent(this, "config-changed", { config: config });
|
||||
}
|
||||
|
||||
private _computeLabelCallback = (
|
||||
@ -117,6 +147,7 @@ export class HuiClimatePresetModesCardFeatureEditor
|
||||
switch (schema.name) {
|
||||
case "style":
|
||||
case "preset_modes":
|
||||
case "customize_modes":
|
||||
return this.hass!.localize(
|
||||
`ui.panel.lovelace.editor.features.types.climate-preset-modes.${schema.name}`
|
||||
);
|
||||
|
@ -17,6 +17,10 @@ import {
|
||||
} from "../../card-features/types";
|
||||
import type { LovelaceCardFeatureEditor } from "../../types";
|
||||
|
||||
type ClimateSwingModesCardFeatureData = ClimateSwingModesCardFeatureConfig & {
|
||||
customize_modes: boolean;
|
||||
};
|
||||
|
||||
@customElement("hui-climate-swing-modes-card-feature-editor")
|
||||
export class HuiClimateSwingModesCardFeatureEditor
|
||||
extends LitElement
|
||||
@ -36,7 +40,8 @@ export class HuiClimateSwingModesCardFeatureEditor
|
||||
(
|
||||
localize: LocalizeFunc,
|
||||
formatEntityAttributeValue: FormatEntityAttributeValueFunc,
|
||||
stateObj?: HassEntity
|
||||
stateObj: HassEntity | undefined,
|
||||
customizeModes: boolean
|
||||
) =>
|
||||
[
|
||||
{
|
||||
@ -55,23 +60,33 @@ export class HuiClimateSwingModesCardFeatureEditor
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "swing_modes",
|
||||
name: "customize_modes",
|
||||
selector: {
|
||||
select: {
|
||||
multiple: true,
|
||||
mode: "list",
|
||||
options:
|
||||
stateObj?.attributes.swing_modes?.map((mode) => ({
|
||||
value: mode,
|
||||
label: formatEntityAttributeValue(
|
||||
stateObj,
|
||||
"swing_mode",
|
||||
mode
|
||||
),
|
||||
})) || [],
|
||||
},
|
||||
boolean: {},
|
||||
},
|
||||
},
|
||||
...(customizeModes
|
||||
? ([
|
||||
{
|
||||
name: "swing_modes",
|
||||
selector: {
|
||||
select: {
|
||||
reorder: true,
|
||||
multiple: true,
|
||||
options:
|
||||
stateObj?.attributes.swing_modes?.map((mode) => ({
|
||||
value: mode,
|
||||
label: formatEntityAttributeValue(
|
||||
stateObj,
|
||||
"swing_mode",
|
||||
mode
|
||||
),
|
||||
})) || [],
|
||||
},
|
||||
},
|
||||
},
|
||||
] as const satisfies readonly HaFormSchema[])
|
||||
: []),
|
||||
] as const satisfies readonly HaFormSchema[]
|
||||
);
|
||||
|
||||
@ -84,16 +99,17 @@ export class HuiClimateSwingModesCardFeatureEditor
|
||||
? this.hass.states[this.context?.entity_id]
|
||||
: undefined;
|
||||
|
||||
const data: ClimateSwingModesCardFeatureConfig = {
|
||||
const data: ClimateSwingModesCardFeatureData = {
|
||||
style: "dropdown",
|
||||
swing_modes: [],
|
||||
...this._config,
|
||||
customize_modes: this._config.swing_modes !== undefined,
|
||||
};
|
||||
|
||||
const schema = this._schema(
|
||||
this.hass.localize,
|
||||
this.hass.formatEntityAttributeValue,
|
||||
stateObj
|
||||
stateObj,
|
||||
data.customize_modes
|
||||
);
|
||||
|
||||
return html`
|
||||
@ -108,7 +124,21 @@ export class HuiClimateSwingModesCardFeatureEditor
|
||||
}
|
||||
|
||||
private _valueChanged(ev: CustomEvent): void {
|
||||
fireEvent(this, "config-changed", { config: ev.detail.value });
|
||||
const { customize_modes, ...config } = ev.detail
|
||||
.value as ClimateSwingModesCardFeatureData;
|
||||
|
||||
const stateObj = this.context?.entity_id
|
||||
? this.hass!.states[this.context?.entity_id]
|
||||
: undefined;
|
||||
|
||||
if (customize_modes && !config.swing_modes) {
|
||||
config.swing_modes = stateObj?.attributes.swing_modes || [];
|
||||
}
|
||||
if (!customize_modes && config.swing_modes) {
|
||||
delete config.swing_modes;
|
||||
}
|
||||
|
||||
fireEvent(this, "config-changed", { config: config });
|
||||
}
|
||||
|
||||
private _computeLabelCallback = (
|
||||
@ -117,6 +147,7 @@ export class HuiClimateSwingModesCardFeatureEditor
|
||||
switch (schema.name) {
|
||||
case "style":
|
||||
case "swing_modes":
|
||||
case "customize_modes":
|
||||
return this.hass!.localize(
|
||||
`ui.panel.lovelace.editor.features.types.climate-swing-modes.${schema.name}`
|
||||
);
|
||||
|
@ -17,6 +17,10 @@ import {
|
||||
} from "../../card-features/types";
|
||||
import type { LovelaceCardFeatureEditor } from "../../types";
|
||||
|
||||
type FanPresetModesCardFeatureData = FanPresetModesCardFeatureConfig & {
|
||||
customize_modes: boolean;
|
||||
};
|
||||
|
||||
@customElement("hui-fan-preset-modes-card-feature-editor")
|
||||
export class HuiFanPresetModesCardFeatureEditor
|
||||
extends LitElement
|
||||
@ -36,7 +40,8 @@ export class HuiFanPresetModesCardFeatureEditor
|
||||
(
|
||||
localize: LocalizeFunc,
|
||||
formatEntityAttributeValue: FormatEntityAttributeValueFunc,
|
||||
stateObj?: HassEntity
|
||||
stateObj: HassEntity | undefined,
|
||||
customizeModes: boolean
|
||||
) =>
|
||||
[
|
||||
{
|
||||
@ -55,23 +60,33 @@ export class HuiFanPresetModesCardFeatureEditor
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "preset_modes",
|
||||
name: "customize_modes",
|
||||
selector: {
|
||||
select: {
|
||||
multiple: true,
|
||||
mode: "list",
|
||||
options:
|
||||
stateObj?.attributes.preset_modes?.map((mode) => ({
|
||||
value: mode,
|
||||
label: formatEntityAttributeValue(
|
||||
stateObj,
|
||||
"preset_mode",
|
||||
mode
|
||||
),
|
||||
})) || [],
|
||||
},
|
||||
boolean: {},
|
||||
},
|
||||
},
|
||||
...(customizeModes
|
||||
? ([
|
||||
{
|
||||
name: "preset_modes",
|
||||
selector: {
|
||||
select: {
|
||||
reorder: true,
|
||||
multiple: true,
|
||||
options:
|
||||
stateObj?.attributes.preset_modes?.map((mode) => ({
|
||||
value: mode,
|
||||
label: formatEntityAttributeValue(
|
||||
stateObj,
|
||||
"preset_mode",
|
||||
mode
|
||||
),
|
||||
})) || [],
|
||||
},
|
||||
},
|
||||
},
|
||||
] as const satisfies readonly HaFormSchema[])
|
||||
: []),
|
||||
] as const satisfies readonly HaFormSchema[]
|
||||
);
|
||||
|
||||
@ -84,16 +99,17 @@ export class HuiFanPresetModesCardFeatureEditor
|
||||
? this.hass.states[this.context?.entity_id]
|
||||
: undefined;
|
||||
|
||||
const data: FanPresetModesCardFeatureConfig = {
|
||||
const data: FanPresetModesCardFeatureData = {
|
||||
style: "dropdown",
|
||||
preset_modes: [],
|
||||
...this._config,
|
||||
customize_modes: this._config.preset_modes !== undefined,
|
||||
};
|
||||
|
||||
const schema = this._schema(
|
||||
this.hass.localize,
|
||||
this.hass.formatEntityAttributeValue,
|
||||
stateObj
|
||||
stateObj,
|
||||
data.customize_modes
|
||||
);
|
||||
|
||||
return html`
|
||||
@ -108,7 +124,21 @@ export class HuiFanPresetModesCardFeatureEditor
|
||||
}
|
||||
|
||||
private _valueChanged(ev: CustomEvent): void {
|
||||
fireEvent(this, "config-changed", { config: ev.detail.value });
|
||||
const { customize_modes, ...config } = ev.detail
|
||||
.value as FanPresetModesCardFeatureData;
|
||||
|
||||
const stateObj = this.context?.entity_id
|
||||
? this.hass!.states[this.context?.entity_id]
|
||||
: undefined;
|
||||
|
||||
if (customize_modes && !config.preset_modes) {
|
||||
config.preset_modes = stateObj?.attributes.preset_modes || [];
|
||||
}
|
||||
if (!customize_modes && config.preset_modes) {
|
||||
delete config.preset_modes;
|
||||
}
|
||||
|
||||
fireEvent(this, "config-changed", { config: config });
|
||||
}
|
||||
|
||||
private _computeLabelCallback = (
|
||||
@ -117,6 +147,7 @@ export class HuiFanPresetModesCardFeatureEditor
|
||||
switch (schema.name) {
|
||||
case "style":
|
||||
case "preset_modes":
|
||||
case "customize_modes":
|
||||
return this.hass!.localize(
|
||||
`ui.panel.lovelace.editor.features.types.fan-preset-modes.${schema.name}`
|
||||
);
|
||||
|
@ -17,6 +17,10 @@ import {
|
||||
} from "../../card-features/types";
|
||||
import type { LovelaceCardFeatureEditor } from "../../types";
|
||||
|
||||
type HumidifierModesCardFeatureData = HumidifierModesCardFeatureConfig & {
|
||||
customize_modes: boolean;
|
||||
};
|
||||
|
||||
@customElement("hui-humidifier-modes-card-feature-editor")
|
||||
export class HuiHumidifierModesCardFeatureEditor
|
||||
extends LitElement
|
||||
@ -36,7 +40,8 @@ export class HuiHumidifierModesCardFeatureEditor
|
||||
(
|
||||
localize: LocalizeFunc,
|
||||
formatEntityAttributeValue: FormatEntityAttributeValueFunc,
|
||||
stateObj?: HassEntity
|
||||
stateObj: HassEntity | undefined,
|
||||
customizeModes: boolean
|
||||
) =>
|
||||
[
|
||||
{
|
||||
@ -55,19 +60,33 @@ export class HuiHumidifierModesCardFeatureEditor
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "modes",
|
||||
name: "customize_modes",
|
||||
selector: {
|
||||
select: {
|
||||
multiple: true,
|
||||
mode: "list",
|
||||
options:
|
||||
stateObj?.attributes.available_modes?.map((mode) => ({
|
||||
value: mode,
|
||||
label: formatEntityAttributeValue(stateObj, "mode", mode),
|
||||
})) || [],
|
||||
},
|
||||
boolean: {},
|
||||
},
|
||||
},
|
||||
...(customizeModes
|
||||
? ([
|
||||
{
|
||||
name: "modes",
|
||||
selector: {
|
||||
select: {
|
||||
reorder: true,
|
||||
multiple: true,
|
||||
options:
|
||||
stateObj?.attributes.available_modes?.map((mode) => ({
|
||||
value: mode,
|
||||
label: formatEntityAttributeValue(
|
||||
stateObj,
|
||||
"mode",
|
||||
mode
|
||||
),
|
||||
})) || [],
|
||||
},
|
||||
},
|
||||
},
|
||||
] as const satisfies readonly HaFormSchema[])
|
||||
: []),
|
||||
] as const satisfies readonly HaFormSchema[]
|
||||
);
|
||||
|
||||
@ -80,16 +99,17 @@ export class HuiHumidifierModesCardFeatureEditor
|
||||
? this.hass.states[this.context?.entity_id]
|
||||
: undefined;
|
||||
|
||||
const data: HumidifierModesCardFeatureConfig = {
|
||||
const data: HumidifierModesCardFeatureData = {
|
||||
style: "dropdown",
|
||||
modes: [],
|
||||
...this._config,
|
||||
customize_modes: this._config.modes !== undefined,
|
||||
};
|
||||
|
||||
const schema = this._schema(
|
||||
this.hass.localize,
|
||||
this.hass.formatEntityAttributeValue,
|
||||
stateObj
|
||||
stateObj,
|
||||
data.customize_modes
|
||||
);
|
||||
|
||||
return html`
|
||||
@ -104,7 +124,21 @@ export class HuiHumidifierModesCardFeatureEditor
|
||||
}
|
||||
|
||||
private _valueChanged(ev: CustomEvent): void {
|
||||
fireEvent(this, "config-changed", { config: ev.detail.value });
|
||||
const { customize_modes, ...config } = ev.detail
|
||||
.value as HumidifierModesCardFeatureData;
|
||||
|
||||
const stateObj = this.context?.entity_id
|
||||
? this.hass!.states[this.context?.entity_id]
|
||||
: undefined;
|
||||
|
||||
if (customize_modes && !config.modes) {
|
||||
config.modes = stateObj?.attributes.available_modes || [];
|
||||
}
|
||||
if (!customize_modes && config.modes) {
|
||||
delete config.modes;
|
||||
}
|
||||
|
||||
fireEvent(this, "config-changed", { config: config });
|
||||
}
|
||||
|
||||
private _computeLabelCallback = (
|
||||
@ -113,6 +147,7 @@ export class HuiHumidifierModesCardFeatureEditor
|
||||
switch (schema.name) {
|
||||
case "style":
|
||||
case "modes":
|
||||
case "customize_modes":
|
||||
return this.hass!.localize(
|
||||
`ui.panel.lovelace.editor.features.types.humidifier-modes.${schema.name}`
|
||||
);
|
||||
|
@ -0,0 +1,140 @@
|
||||
import { HassEntity } from "home-assistant-js-websocket";
|
||||
import { 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 { FormatEntityStateFunc } from "../../../../common/translations/entity-state";
|
||||
import "../../../../components/ha-form/ha-form";
|
||||
import type {
|
||||
HaFormSchema,
|
||||
SchemaUnion,
|
||||
} from "../../../../components/ha-form/types";
|
||||
import type { HomeAssistant } from "../../../../types";
|
||||
import {
|
||||
LovelaceCardFeatureContext,
|
||||
SelectOptionsCardFeatureConfig,
|
||||
} from "../../card-features/types";
|
||||
import type { LovelaceCardFeatureEditor } from "../../types";
|
||||
|
||||
type SelectOptionsCardFeatureData = SelectOptionsCardFeatureConfig & {
|
||||
customize_options: boolean;
|
||||
};
|
||||
|
||||
@customElement("hui-select-options-card-feature-editor")
|
||||
export class HuiSelectOptionsCardFeatureEditor
|
||||
extends LitElement
|
||||
implements LovelaceCardFeatureEditor
|
||||
{
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public context?: LovelaceCardFeatureContext;
|
||||
|
||||
@state() private _config?: SelectOptionsCardFeatureConfig;
|
||||
|
||||
public setConfig(config: SelectOptionsCardFeatureConfig): void {
|
||||
this._config = config;
|
||||
}
|
||||
|
||||
private _schema = memoizeOne(
|
||||
(
|
||||
formatEntityState: FormatEntityStateFunc,
|
||||
stateObj: HassEntity | undefined,
|
||||
customizeOptions: boolean
|
||||
) =>
|
||||
[
|
||||
{
|
||||
name: "customize_options",
|
||||
selector: {
|
||||
boolean: {},
|
||||
},
|
||||
},
|
||||
...(customizeOptions
|
||||
? ([
|
||||
{
|
||||
name: "options",
|
||||
selector: {
|
||||
select: {
|
||||
multiple: true,
|
||||
reorder: true,
|
||||
options:
|
||||
stateObj?.attributes.options?.map((option) => ({
|
||||
value: option,
|
||||
label: formatEntityState(stateObj, option),
|
||||
})) || [],
|
||||
},
|
||||
},
|
||||
},
|
||||
] as const satisfies readonly HaFormSchema[])
|
||||
: []),
|
||||
] as const satisfies readonly HaFormSchema[]
|
||||
);
|
||||
|
||||
protected render() {
|
||||
if (!this.hass || !this._config) {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
const stateObj = this.context?.entity_id
|
||||
? this.hass.states[this.context?.entity_id]
|
||||
: undefined;
|
||||
|
||||
const data: SelectOptionsCardFeatureData = {
|
||||
...this._config,
|
||||
customize_options: this._config.options !== undefined,
|
||||
};
|
||||
|
||||
const schema = this._schema(
|
||||
this.hass.formatEntityState,
|
||||
stateObj,
|
||||
data.customize_options
|
||||
);
|
||||
|
||||
return html`
|
||||
<ha-form
|
||||
.hass=${this.hass}
|
||||
.data=${data}
|
||||
.schema=${schema}
|
||||
.computeLabel=${this._computeLabelCallback}
|
||||
@value-changed=${this._valueChanged}
|
||||
></ha-form>
|
||||
`;
|
||||
}
|
||||
|
||||
private _valueChanged(ev: CustomEvent): void {
|
||||
const { customize_options, ...config } = ev.detail
|
||||
.value as SelectOptionsCardFeatureData;
|
||||
|
||||
const stateObj = this.context?.entity_id
|
||||
? this.hass!.states[this.context?.entity_id]
|
||||
: undefined;
|
||||
|
||||
if (customize_options && !config.options) {
|
||||
config.options = stateObj?.attributes.options || [];
|
||||
}
|
||||
if (!customize_options && config.options) {
|
||||
delete config.options;
|
||||
}
|
||||
|
||||
fireEvent(this, "config-changed", { config: config });
|
||||
}
|
||||
|
||||
private _computeLabelCallback = (
|
||||
schema: SchemaUnion<ReturnType<typeof this._schema>>
|
||||
) => {
|
||||
switch (schema.name) {
|
||||
case "options":
|
||||
case "customize_options":
|
||||
return this.hass!.localize(
|
||||
`ui.panel.lovelace.editor.features.types.select-options.${schema.name}`
|
||||
);
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"hui-select-options-card-feature-editor": HuiSelectOptionsCardFeatureEditor;
|
||||
}
|
||||
}
|
@ -5,14 +5,22 @@ import memoizeOne from "memoize-one";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import type { FormatEntityStateFunc } from "../../../../common/translations/entity-state";
|
||||
import "../../../../components/ha-form/ha-form";
|
||||
import type { SchemaUnion } from "../../../../components/ha-form/types";
|
||||
import type {
|
||||
HaFormSchema,
|
||||
SchemaUnion,
|
||||
} from "../../../../components/ha-form/types";
|
||||
import type { HomeAssistant } from "../../../../types";
|
||||
import {
|
||||
WaterHeaterOperationModesCardFeatureConfig,
|
||||
LovelaceCardFeatureContext,
|
||||
} from "../../card-features/types";
|
||||
import type { LovelaceCardFeatureEditor } from "../../types";
|
||||
import { OPERATION_MODES } from "../../../../data/water_heater";
|
||||
import { compareWaterHeaterOperationMode } from "../../../../data/water_heater";
|
||||
|
||||
type WaterHeaterOperationModesCardFeatureData =
|
||||
WaterHeaterOperationModesCardFeatureConfig & {
|
||||
customize_modes: boolean;
|
||||
};
|
||||
|
||||
@customElement("hui-water-heater-operation-modes-card-feature-editor")
|
||||
export class HuiWaterHeaterOperationModesCardFeatureEditor
|
||||
@ -30,24 +38,41 @@ export class HuiWaterHeaterOperationModesCardFeatureEditor
|
||||
}
|
||||
|
||||
private _schema = memoizeOne(
|
||||
(formatEntityState: FormatEntityStateFunc, stateObj?: HassEntity) =>
|
||||
(
|
||||
formatEntityState: FormatEntityStateFunc,
|
||||
stateObj: HassEntity | undefined,
|
||||
customizeModes: boolean
|
||||
) =>
|
||||
[
|
||||
{
|
||||
name: "operation_modes",
|
||||
name: "customize_modes",
|
||||
selector: {
|
||||
select: {
|
||||
multiple: true,
|
||||
mode: "list",
|
||||
options: OPERATION_MODES.filter((mode) =>
|
||||
stateObj?.attributes.operation_list?.includes(mode)
|
||||
).map((mode) => ({
|
||||
value: mode,
|
||||
label: stateObj ? formatEntityState(stateObj, mode) : mode,
|
||||
})),
|
||||
},
|
||||
boolean: {},
|
||||
},
|
||||
},
|
||||
] as const
|
||||
...(customizeModes
|
||||
? ([
|
||||
{
|
||||
name: "operation_modes",
|
||||
selector: {
|
||||
select: {
|
||||
reorder: true,
|
||||
multiple: true,
|
||||
options: (stateObj?.attributes.operation_list || [])
|
||||
.concat()
|
||||
.sort(compareWaterHeaterOperationMode)
|
||||
.map((mode) => ({
|
||||
value: mode,
|
||||
label: stateObj
|
||||
? formatEntityState(stateObj, mode)
|
||||
: mode,
|
||||
})),
|
||||
},
|
||||
},
|
||||
},
|
||||
] as const satisfies readonly HaFormSchema[])
|
||||
: []),
|
||||
] as const satisfies readonly HaFormSchema[]
|
||||
);
|
||||
|
||||
protected render() {
|
||||
@ -59,12 +84,21 @@ export class HuiWaterHeaterOperationModesCardFeatureEditor
|
||||
? this.hass.states[this.context?.entity_id]
|
||||
: undefined;
|
||||
|
||||
const schema = this._schema(this.hass.formatEntityState, stateObj);
|
||||
const data: WaterHeaterOperationModesCardFeatureData = {
|
||||
...this._config,
|
||||
customize_modes: this._config.operation_modes !== undefined,
|
||||
};
|
||||
|
||||
const schema = this._schema(
|
||||
this.hass.formatEntityState,
|
||||
stateObj,
|
||||
data.customize_modes
|
||||
);
|
||||
|
||||
return html`
|
||||
<ha-form
|
||||
.hass=${this.hass}
|
||||
.data=${this._config}
|
||||
.data=${data}
|
||||
.schema=${schema}
|
||||
.computeLabel=${this._computeLabelCallback}
|
||||
@value-changed=${this._valueChanged}
|
||||
@ -73,7 +107,23 @@ export class HuiWaterHeaterOperationModesCardFeatureEditor
|
||||
}
|
||||
|
||||
private _valueChanged(ev: CustomEvent): void {
|
||||
fireEvent(this, "config-changed", { config: ev.detail.value });
|
||||
const { customize_modes, ...config } = ev.detail
|
||||
.value as WaterHeaterOperationModesCardFeatureData;
|
||||
|
||||
const stateObj = this.context?.entity_id
|
||||
? this.hass!.states[this.context?.entity_id]
|
||||
: undefined;
|
||||
|
||||
if (customize_modes && !config.operation_modes) {
|
||||
config.operation_modes = (stateObj?.attributes.operation_list || [])
|
||||
.concat()
|
||||
.sort(compareWaterHeaterOperationMode);
|
||||
}
|
||||
if (!customize_modes && config.operation_modes) {
|
||||
delete config.operation_modes;
|
||||
}
|
||||
|
||||
fireEvent(this, "config-changed", { config: config });
|
||||
}
|
||||
|
||||
private _computeLabelCallback = (
|
||||
@ -81,13 +131,12 @@ export class HuiWaterHeaterOperationModesCardFeatureEditor
|
||||
) => {
|
||||
switch (schema.name) {
|
||||
case "operation_modes":
|
||||
case "customize_modes":
|
||||
return this.hass!.localize(
|
||||
`ui.panel.lovelace.editor.features.types.water-heater-modes.${schema.name}`
|
||||
`ui.panel.lovelace.editor.features.types.water-heater-operation-modes.${schema.name}`
|
||||
);
|
||||
default:
|
||||
return this.hass!.localize(
|
||||
`ui.panel.lovelace.editor.card.generic.${schema.name}`
|
||||
);
|
||||
return "";
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -1013,8 +1013,6 @@ class HUIRoot extends LitElement {
|
||||
padding-inline-start: env(safe-area-inset-left);
|
||||
padding-inline-end: env(safe-area-inset-right);
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
}
|
||||
hui-view {
|
||||
background: var(
|
||||
--lovelace-background,
|
||||
var(--primary-background-color)
|
||||
|
@ -1889,6 +1889,7 @@
|
||||
"check_updates": "Check for updates",
|
||||
"no_new_updates": "No new updates found",
|
||||
"updates_refreshed": "{count} {count, plural,\n one {update}\n other {updates}\n} refreshed",
|
||||
"checking_updates": "Checking for updates...",
|
||||
"title": "{count} {count, plural,\n one {update}\n other {updates}\n}",
|
||||
"unable_to_fetch": "Unable to load updates",
|
||||
"more_updates": "Show all updates",
|
||||
@ -4061,12 +4062,14 @@
|
||||
"search": "Search {number} entities",
|
||||
"unnamed_entity": "Unnamed entity",
|
||||
"status": {
|
||||
"restored": "Restored",
|
||||
"available": "Available",
|
||||
"unavailable": "Unavailable",
|
||||
"enabled": "Enabled",
|
||||
"disabled": "Disabled",
|
||||
"readonly": "Read-only",
|
||||
"hidden": "Hidden"
|
||||
"visible": "Visible",
|
||||
"hidden": "Hidden",
|
||||
"not_provided": "Not provided",
|
||||
"unmanageable": "Unmanageable"
|
||||
},
|
||||
"headers": {
|
||||
"state_icon": "State icon",
|
||||
@ -4076,7 +4079,10 @@
|
||||
"area": "Area",
|
||||
"disabled_by": "Disabled by",
|
||||
"status": "Status",
|
||||
"domain": "Domain"
|
||||
"domain": "Domain",
|
||||
"availability": "Availability",
|
||||
"visibility": "Visibility",
|
||||
"enabled": "Enabled"
|
||||
},
|
||||
"selected": "{number} selected",
|
||||
"enable_selected": {
|
||||
@ -5996,16 +6002,18 @@
|
||||
"dropdown": "[%key:ui::panel::lovelace::editor::features::types::climate-preset-modes::style_list::dropdown%]",
|
||||
"icons": "[%key:ui::panel::lovelace::editor::features::types::climate-preset-modes::style_list::icons%]"
|
||||
},
|
||||
"customize_modes": "Customize fan modes",
|
||||
"fan_modes": "Fan modes"
|
||||
},
|
||||
"climate-swing-modes": {
|
||||
"label": "Climate swing modes",
|
||||
"swing_modes": "Swing modes",
|
||||
"style": "[%key:ui::panel::lovelace::editor::features::types::climate-preset-modes::style%]",
|
||||
"style_list": {
|
||||
"dropdown": "[%key:ui::panel::lovelace::editor::features::types::climate-preset-modes::style_list::dropdown%]",
|
||||
"icons": "[%key:ui::panel::lovelace::editor::features::types::climate-preset-modes::style_list::icons%]"
|
||||
},
|
||||
"swing_modes": "Swing modes"
|
||||
"customize_modes": "Customize swing modes"
|
||||
},
|
||||
"climate-hvac-modes": {
|
||||
"label": "Climate HVAC modes",
|
||||
@ -6014,7 +6022,8 @@
|
||||
"style_list": {
|
||||
"dropdown": "[%key:ui::panel::lovelace::editor::features::types::climate-preset-modes::style_list::dropdown%]",
|
||||
"icons": "[%key:ui::panel::lovelace::editor::features::types::climate-preset-modes::style_list::icons%]"
|
||||
}
|
||||
},
|
||||
"customize_modes": "Customize HVAC modes"
|
||||
},
|
||||
"climate-preset-modes": {
|
||||
"label": "Climate preset modes",
|
||||
@ -6023,6 +6032,7 @@
|
||||
"dropdown": "Dropdown",
|
||||
"icons": "Icons"
|
||||
},
|
||||
"customize_modes": "Customize preset modes",
|
||||
"preset_modes": "Preset modes"
|
||||
},
|
||||
"fan-preset-modes": {
|
||||
@ -6032,6 +6042,7 @@
|
||||
"dropdown": "[%key:ui::panel::lovelace::editor::features::types::climate-preset-modes::style_list::dropdown%]",
|
||||
"icons": "[%key:ui::panel::lovelace::editor::features::types::climate-preset-modes::style_list::icons%]"
|
||||
},
|
||||
"customize_modes": "[%key:ui::panel::lovelace::editor::features::types::climate-preset-modes::customize_modes%]",
|
||||
"preset_modes": "[%key:ui::panel::lovelace::editor::features::types::climate-preset-modes::preset_modes%]"
|
||||
},
|
||||
"humidifier-toggle": {
|
||||
@ -6044,10 +6055,13 @@
|
||||
"dropdown": "[%key:ui::panel::lovelace::editor::features::types::climate-preset-modes::style_list::dropdown%]",
|
||||
"icons": "[%key:ui::panel::lovelace::editor::features::types::climate-preset-modes::style_list::icons%]"
|
||||
},
|
||||
"customize_modes": "Customize modes",
|
||||
"modes": "Modes"
|
||||
},
|
||||
"select-options": {
|
||||
"label": "Select options"
|
||||
"label": "Select options",
|
||||
"options": "Options",
|
||||
"customize_options": "Customize options"
|
||||
},
|
||||
"numeric-input": {
|
||||
"label": "Numeric input",
|
||||
@ -6065,7 +6079,8 @@
|
||||
},
|
||||
"water-heater-operation-modes": {
|
||||
"label": "Water heater operation modes",
|
||||
"operation_modes": "Operation modes"
|
||||
"operation_modes": "Operation modes",
|
||||
"customize_modes": "Customize operation modes"
|
||||
},
|
||||
"lawn-mower-commands": {
|
||||
"label": "Lawn mower commands",
|
||||
|
Loading…
x
Reference in New Issue
Block a user