Update humidifier Ui with current humidity and action (#17072)

This commit is contained in:
Paul Bottein 2023-06-28 16:14:18 +02:00 committed by GitHub
parent c0613545e7
commit eaeb37da4d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 331 additions and 42 deletions

View File

@ -182,6 +182,7 @@ export const DOMAINS_WITH_CARD = [
"input_select",
"input_number",
"input_text",
"humidifier",
"lock",
"media_player",
"number",

View File

@ -135,6 +135,7 @@ const FIXED_DOMAIN_ATTRIBUTE_STATES = {
},
humidifier: {
device_class: ["humidifier", "dehumidifier"],
action: ["off", "idle", "humidifying", "drying"],
},
media_player: {
device_class: ["tv", "speaker", "receiver"],

View File

@ -0,0 +1,137 @@
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property } from "lit/decorators";
import { computeAttributeValueDisplay } from "../common/entity/compute_attribute_display";
import { computeStateDisplay } from "../common/entity/compute_state_display";
import { formatNumber } from "../common/number/format_number";
import { blankBeforePercent } from "../common/translations/blank_before_percent";
import { isUnavailableState, OFF } from "../data/entity";
import { HumidifierEntity } from "../data/humidifier";
import type { HomeAssistant } from "../types";
@customElement("ha-humidifier-state")
class HaHumidifierState extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public stateObj!: HumidifierEntity;
protected render(): TemplateResult {
const currentStatus = this._computeCurrentStatus();
return html`<div class="target">
${!isUnavailableState(this.stateObj.state)
? html`<span class="state-label">
${this._localizeState()}
${this.stateObj.attributes.mode
? html`-
${computeAttributeValueDisplay(
this.hass.localize,
this.stateObj,
this.hass.locale,
this.hass.config,
this.hass.entities,
"mode"
)}`
: ""}
</span>
<div class="unit">${this._computeTarget()}</div>`
: this._localizeState()}
</div>
${currentStatus && !isUnavailableState(this.stateObj.state)
? html`<div class="current">
${this.hass.localize("ui.card.climate.currently")}:
<div class="unit">${currentStatus}</div>
</div>`
: ""}`;
}
private _computeCurrentStatus(): string | undefined {
if (!this.hass || !this.stateObj) {
return undefined;
}
if (this.stateObj.attributes.current_humidity != null) {
return `${formatNumber(
this.stateObj.attributes.current_humidity,
this.hass.locale
)}${blankBeforePercent(this.hass.locale)}%`;
}
return undefined;
}
private _computeTarget(): string {
if (!this.hass || !this.stateObj) {
return "";
}
if (this.stateObj.attributes.humidity != null) {
return `${formatNumber(
this.stateObj.attributes.humidity,
this.hass.locale
)}${blankBeforePercent(this.hass.locale)}%`;
}
return "";
}
private _localizeState(): string {
if (isUnavailableState(this.stateObj.state)) {
return this.hass.localize(`state.default.${this.stateObj.state}`);
}
const stateString = computeStateDisplay(
this.hass.localize,
this.stateObj,
this.hass.locale,
this.hass.config,
this.hass.entities
);
return this.stateObj.attributes.action && this.stateObj.state !== OFF
? `${computeAttributeValueDisplay(
this.hass.localize,
this.stateObj,
this.hass.locale,
this.hass.config,
this.hass.entities,
"action"
)} (${stateString})`
: stateString;
}
static get styles(): CSSResultGroup {
return css`
:host {
display: flex;
flex-direction: column;
justify-content: center;
white-space: nowrap;
}
.target {
color: var(--primary-text-color);
}
.current {
color: var(--secondary-text-color);
}
.state-label {
font-weight: bold;
text-transform: capitalize;
}
.unit {
display: inline-block;
direction: ltr;
}
`;
}
}
declare global {
interface HTMLElementTagNameMap {
"ha-humidifier-state": HaHumidifierState;
}
}

View File

@ -2,21 +2,19 @@ import {
HassEntityAttributeBase,
HassEntityBase,
} from "home-assistant-js-websocket";
import { FIXED_DOMAIN_STATES } from "../common/entity/get_states";
import { UNAVAILABLE_STATES } from "./entity";
type HumidifierState =
| (typeof FIXED_DOMAIN_STATES.humidifier)[number]
| (typeof UNAVAILABLE_STATES)[number];
export type HumidifierState = "on" | "off";
export type HumidifierAction = "off" | "idle" | "humidifying" | "drying";
export type HumidifierEntity = HassEntityBase & {
state: HumidifierState;
attributes: HassEntityAttributeBase & {
humidity?: number;
current_humidity?: number;
min_humidity?: number;
max_humidity?: number;
mode?: string;
action: HumidifierAction;
available_modes?: string[];
};
};

View File

@ -10,7 +10,10 @@ import {
import { property } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import { fireEvent } from "../../../common/dom/fire_event";
import { computeAttributeValueDisplay } from "../../../common/entity/compute_attribute_display";
import {
computeAttributeNameDisplay,
computeAttributeValueDisplay,
} from "../../../common/entity/compute_attribute_display";
import { stopPropagation } from "../../../common/dom/stop_propagation";
import { supportsFeature } from "../../../common/entity/supports-feature";
import { computeRTLDirection } from "../../../common/util/compute_rtl";
@ -22,6 +25,7 @@ import {
HUMIDIFIER_SUPPORT_MODES,
} from "../../../data/humidifier";
import { HomeAssistant } from "../../../types";
import { computeStateDisplay } from "../../../common/entity/compute_state_display";
class MoreInfoHumidifier extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@ -49,7 +53,14 @@ class MoreInfoHumidifier extends LitElement {
})}
>
<div class="container-humidity">
<div>${hass.localize("ui.card.humidifier.humidity")}</div>
<div>
${computeAttributeNameDisplay(
hass.localize,
stateObj,
hass.entities,
"humidity"
)}
</div>
<div class="single-row">
<div class="target-humidity">${stateObj.attributes.humidity} %</div>
<ha-slider
@ -65,6 +76,35 @@ class MoreInfoHumidifier extends LitElement {
</ha-slider>
</div>
</div>
<ha-select
.label=${hass.localize("ui.card.humidifier.state")}
.value=${stateObj.state}
fixedMenuPosition
naturalMenuWidth
@selected=${this._handleStateChanged}
@closed=${stopPropagation}
>
<mwc-list-item value="off">
${computeStateDisplay(
hass.localize,
stateObj,
hass.locale,
this.hass.config,
hass.entities,
"off"
)}
</mwc-list-item>
<mwc-list-item value="on">
${computeStateDisplay(
hass.localize,
stateObj,
hass.locale,
this.hass.config,
hass.entities,
"on"
)}
</mwc-list-item>
</ha-select>
${supportModes
? html`
@ -123,6 +163,16 @@ class MoreInfoHumidifier extends LitElement {
);
}
private _handleStateChanged(ev) {
const newVal = ev.target.value || null;
this._callServiceHelper(
this.stateObj!.state,
newVal,
newVal === "on" ? "turn_on" : "turn_off",
{}
);
}
private _handleModeChanged(ev) {
const newVal = ev.target.value || null;
this._callServiceHelper(
@ -179,6 +229,11 @@ class MoreInfoHumidifier extends LitElement {
ha-select {
width: 100%;
margin-top: 8px;
}
ha-slider {
width: 100%;
}
.container-humidity .single-row {

View File

@ -225,15 +225,6 @@ export class HuiTileCard extends LitElement implements LovelaceCard {
}
}
if (domain === "humidifier" && stateActive(stateObj)) {
const humidity = (stateObj as HumidifierEntity).attributes.humidity;
if (humidity) {
return `${Math.round(humidity)}${blankBeforePercent(
this.hass!.locale
)}%`;
}
}
const stateDisplay = computeStateDisplay(
this.hass!.localize,
stateObj,
@ -251,6 +242,16 @@ export class HuiTileCard extends LitElement implements LovelaceCard {
return `${stateDisplay}${positionStateDisplay}`;
}
}
if (domain === "humidifier" && stateActive(stateObj)) {
const humidity = (stateObj as HumidifierEntity).attributes.humidity;
if (humidity) {
return `${stateDisplay}${Math.round(humidity)}${blankBeforePercent(
this.hass!.locale
)}%`;
}
}
return stateDisplay;
}

View File

@ -0,0 +1,41 @@
import {
mdiArrowDownBold,
mdiArrowUpBold,
mdiClockOutline,
mdiPower,
} from "@mdi/js";
import { stateColorCss } from "../../../../../common/entity/state_color";
import {
HumidifierAction,
HumidifierEntity,
HumidifierState,
} from "../../../../../data/humidifier";
import { ComputeBadgeFunction } from "./tile-badge";
export const HUMIDIFIER_ACTION_ICONS: Record<HumidifierAction, string> = {
drying: mdiArrowDownBold,
humidifying: mdiArrowUpBold,
idle: mdiClockOutline,
off: mdiPower,
};
export const HUMIDIFIER_ACTION_MODE: Record<HumidifierAction, HumidifierState> =
{
drying: "on",
humidifying: "on",
idle: "off",
off: "off",
};
export const computeHumidifierBadge: ComputeBadgeFunction = (stateObj) => {
const hvacAction = (stateObj as HumidifierEntity).attributes.action;
if (!hvacAction || hvacAction === "off") {
return undefined;
}
return {
iconPath: HUMIDIFIER_ACTION_ICONS[hvacAction],
color: stateColorCss(stateObj, HUMIDIFIER_ACTION_MODE[hvacAction]),
};
};

View File

@ -5,6 +5,7 @@ import { UNAVAILABLE, UNKNOWN } from "../../../../../data/entity";
import { HomeAssistant } from "../../../../../types";
import { computeClimateBadge } from "./tile-badge-climate";
import { computePersonBadge } from "./tile-badge-person";
import { computeHumidifierBadge } from "./tile-badge-humidifier";
export type TileBadge = {
color?: string;
@ -34,6 +35,8 @@ export const computeTileBadge: ComputeBadgeFunction = (stateObj, hass) => {
return computePersonBadge(stateObj, hass);
case "climate":
return computeClimateBadge(stateObj, hass);
case "humidifier":
return computeHumidifierBadge(stateObj, hass);
default:
return undefined;
}

View File

@ -1,11 +1,18 @@
import { html, LitElement, PropertyValues, nothing } from "lit";
import {
CSSResultGroup,
LitElement,
PropertyValues,
css,
html,
nothing,
} from "lit";
import { customElement, property } from "lit/decorators";
import "../../../components/entity/ha-entity-toggle";
import "../../../components/ha-humidifier-state";
import { HumidifierEntity } from "../../../data/humidifier";
import { HomeAssistant } from "../../../types";
import { hasConfigOrEntityChanged } from "../common/has-changed";
import "../components/hui-generic-entity-row";
import { computeAttributeValueDisplay } from "../../../common/entity/compute_attribute_display";
import { createEntityNotFoundWarning } from "../components/hui-warning";
import { EntityConfig, LovelaceRow } from "./types";
@ -43,32 +50,20 @@ class HuiHumidifierEntityRow extends LitElement implements LovelaceRow {
}
return html`
<hui-generic-entity-row
.hass=${this.hass}
.config=${this._config}
.secondaryText=${stateObj.attributes.humidity
? `${this.hass!.localize("ui.card.humidifier.humidity")}:
${stateObj.attributes.humidity} %${
stateObj.attributes.mode
? ` (${computeAttributeValueDisplay(
this.hass.localize,
stateObj,
this.hass.locale,
this.hass.config,
this.hass.entities,
"mode"
)})`
: ""
}`
: ""}
>
<ha-entity-toggle
.hass=${this.hass}
.stateObj=${stateObj}
></ha-entity-toggle>
<hui-generic-entity-row .hass=${this.hass} .config=${this._config}>
<ha-humidifier-state .hass=${this.hass} .stateObj=${stateObj}>
</ha-humidifier-state>
</hui-generic-entity-row>
`;
}
static get styles(): CSSResultGroup {
return css`
ha-humidifier-state {
text-align: right;
}
`;
}
}
declare global {

View File

@ -4,6 +4,7 @@ import dynamicContentUpdater from "../common/dom/dynamic_content_updater";
import { stateCardType } from "../common/entity/state_card_type";
import "./state-card-button";
import "./state-card-climate";
import "./state-card-humidifier";
import "./state-card-configurator";
import "./state-card-cover";
import "./state-card-display";

View File

@ -0,0 +1,55 @@
import "@polymer/iron-flex-layout/iron-flex-layout-classes";
import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element";
import "../components/entity/state-info";
import "../components/ha-humidifier-state";
class StateCardHumidifier extends PolymerElement {
static get template() {
return html`
<style include="iron-flex iron-flex-alignment"></style>
<style>
:host {
@apply --paper-font-body1;
line-height: 1.5;
}
ha-humidifier-state {
margin-left: 16px;
text-align: right;
}
</style>
<div class="horizontal justified layout">
${this.stateInfoTemplate}
<ha-humidifier-state
hass="[[hass]]"
state-obj="[[stateObj]]"
></ha-humidifier-state>
</div>
`;
}
static get stateInfoTemplate() {
return html`
<state-info
hass="[[hass]]"
state-obj="[[stateObj]]"
in-dialog="[[inDialog]]"
></state-info>
`;
}
static get properties() {
return {
hass: Object,
stateObj: Object,
inDialog: {
type: Boolean,
value: false,
},
};
}
}
customElements.define("state-card-humidifier", StateCardHumidifier);

View File

@ -131,6 +131,7 @@
},
"humidifier": {
"humidity": "Target humidity",
"state": "State",
"mode": "Mode",
"target_humidity_entity": "{name} target humidity",
"on_entity": "{name} on"