mirror of
https://github.com/home-assistant/frontend.git
synced 2025-11-12 04:20:28 +00:00
Compare commits
4 Commits
copilot/fi
...
Thermostat
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9a49956e98 | ||
|
|
640d6e9549 | ||
|
|
cb043200cc | ||
|
|
6b46949eb7 |
@@ -19,6 +19,7 @@ import { UNIT_F } from "../../../common/const";
|
|||||||
import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element";
|
import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element";
|
||||||
import { fireEvent } from "../../../common/dom/fire_event";
|
import { fireEvent } from "../../../common/dom/fire_event";
|
||||||
import { computeStateName } from "../../../common/entity/compute_state_name";
|
import { computeStateName } from "../../../common/entity/compute_state_name";
|
||||||
|
import { debounce } from "../../../common/util/debounce";
|
||||||
import "../../../components/ha-card";
|
import "../../../components/ha-card";
|
||||||
import type { HaCard } from "../../../components/ha-card";
|
import type { HaCard } from "../../../components/ha-card";
|
||||||
import "../../../components/ha-icon-button";
|
import "../../../components/ha-icon-button";
|
||||||
@@ -30,8 +31,10 @@ import {
|
|||||||
} from "../../../data/climate";
|
} from "../../../data/climate";
|
||||||
import { UNAVAILABLE } from "../../../data/entity";
|
import { UNAVAILABLE } from "../../../data/entity";
|
||||||
import { HomeAssistant } from "../../../types";
|
import { HomeAssistant } from "../../../types";
|
||||||
|
import { actionHandler } from "../common/directives/action-handler-directive";
|
||||||
import { findEntities } from "../common/find-entites";
|
import { findEntities } from "../common/find-entites";
|
||||||
import { hasConfigOrEntityChanged } from "../common/has-changed";
|
import { hasConfigOrEntityChanged } from "../common/has-changed";
|
||||||
|
import { installResizeObserver } from "../common/install-resize-observer";
|
||||||
import { createEntityNotFoundWarning } from "../components/hui-warning";
|
import { createEntityNotFoundWarning } from "../components/hui-warning";
|
||||||
import { LovelaceCard, LovelaceCardEditor } from "../types";
|
import { LovelaceCard, LovelaceCardEditor } from "../types";
|
||||||
import { ThermostatCardConfig } from "./types";
|
import { ThermostatCardConfig } from "./types";
|
||||||
@@ -79,8 +82,25 @@ export class HuiThermostatCard extends LitElement implements LovelaceCard {
|
|||||||
|
|
||||||
@internalProperty() private _setTemp?: number | number[];
|
@internalProperty() private _setTemp?: number | number[];
|
||||||
|
|
||||||
|
@internalProperty() private _lastSetMode?: number;
|
||||||
|
|
||||||
|
@internalProperty() private _narrow = false;
|
||||||
|
|
||||||
@query("ha-card") private _card?: HaCard;
|
@query("ha-card") private _card?: HaCard;
|
||||||
|
|
||||||
|
private _resizeObserver?: ResizeObserver;
|
||||||
|
|
||||||
|
public connectedCallback(): void {
|
||||||
|
super.connectedCallback();
|
||||||
|
this.updateComplete.then(() => this._attachObserver());
|
||||||
|
}
|
||||||
|
|
||||||
|
public disconnectedCallback(): void {
|
||||||
|
if (this._resizeObserver) {
|
||||||
|
this._resizeObserver.disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public getCardSize(): number {
|
public getCardSize(): number {
|
||||||
return 5;
|
return 5;
|
||||||
}
|
}
|
||||||
@@ -214,6 +234,7 @@ export class HuiThermostatCard extends LitElement implements LovelaceCard {
|
|||||||
<ha-card
|
<ha-card
|
||||||
class=${classMap({
|
class=${classMap({
|
||||||
[mode]: true,
|
[mode]: true,
|
||||||
|
narrow: this._narrow,
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<mwc-icon-button
|
<mwc-icon-button
|
||||||
@@ -237,12 +258,40 @@ export class HuiThermostatCard extends LitElement implements LovelaceCard {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="info">
|
<div id="info">
|
||||||
|
${!this._narrow
|
||||||
|
? html`
|
||||||
|
<div class="step-icons">
|
||||||
|
<ha-icon-button
|
||||||
|
class="minus step-icon ${classMap({
|
||||||
|
disabled:
|
||||||
|
Array.isArray(this._setTemp) && !this._lastSetMode,
|
||||||
|
})}"
|
||||||
|
icon="hass:minus"
|
||||||
|
@action=${this._handleStepAction}
|
||||||
|
.actionHandler=${actionHandler()}
|
||||||
|
tempDiff="-1"
|
||||||
|
tabindex="0"
|
||||||
|
></ha-icon-button
|
||||||
|
><ha-icon-button
|
||||||
|
class="plus step-icon ${classMap({
|
||||||
|
disabled:
|
||||||
|
Array.isArray(this._setTemp) && !this._lastSetMode,
|
||||||
|
})}"
|
||||||
|
icon="hass:plus"
|
||||||
|
@action=${this._handleStepAction}
|
||||||
|
.actionHandler=${actionHandler()}
|
||||||
|
tabindex="0"
|
||||||
|
tempDiff="1"
|
||||||
|
></ha-icon-button>
|
||||||
|
</div>
|
||||||
<div id="modes">
|
<div id="modes">
|
||||||
${(stateObj.attributes.hvac_modes || [])
|
${(stateObj.attributes.hvac_modes || [])
|
||||||
.concat()
|
.concat()
|
||||||
.sort(compareClimateHvacModes)
|
.sort(compareClimateHvacModes)
|
||||||
.map((modeItem) => this._renderIcon(modeItem, mode))}
|
.map((modeItem) => this._renderIcon(modeItem, mode))}
|
||||||
</div>
|
</div>
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
${name}
|
${name}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -254,6 +303,11 @@ export class HuiThermostatCard extends LitElement implements LovelaceCard {
|
|||||||
return hasConfigOrEntityChanged(this, changedProps);
|
return hasConfigOrEntityChanged(this, changedProps);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected firstUpdated(): void {
|
||||||
|
this._measureCard();
|
||||||
|
this._attachObserver();
|
||||||
|
}
|
||||||
|
|
||||||
protected updated(changedProps: PropertyValues): void {
|
protected updated(changedProps: PropertyValues): void {
|
||||||
super.updated(changedProps);
|
super.updated(changedProps);
|
||||||
|
|
||||||
@@ -356,12 +410,14 @@ export class HuiThermostatCard extends LitElement implements LovelaceCard {
|
|||||||
const stateObj = this.hass!.states[this._config!.entity] as ClimateEntity;
|
const stateObj = this.hass!.states[this._config!.entity] as ClimateEntity;
|
||||||
|
|
||||||
if (e.detail.low) {
|
if (e.detail.low) {
|
||||||
|
this._lastSetMode = 0;
|
||||||
this.hass!.callService("climate", "set_temperature", {
|
this.hass!.callService("climate", "set_temperature", {
|
||||||
entity_id: this._config!.entity,
|
entity_id: this._config!.entity,
|
||||||
target_temp_low: e.detail.low,
|
target_temp_low: e.detail.low,
|
||||||
target_temp_high: stateObj.attributes.target_temp_high,
|
target_temp_high: stateObj.attributes.target_temp_high,
|
||||||
});
|
});
|
||||||
} else if (e.detail.high) {
|
} else if (e.detail.high) {
|
||||||
|
this._lastSetMode = 1;
|
||||||
this.hass!.callService("climate", "set_temperature", {
|
this.hass!.callService("climate", "set_temperature", {
|
||||||
entity_id: this._config!.entity,
|
entity_id: this._config!.entity,
|
||||||
target_temp_low: stateObj.attributes.target_temp_low,
|
target_temp_low: stateObj.attributes.target_temp_low,
|
||||||
@@ -381,10 +437,11 @@ export class HuiThermostatCard extends LitElement implements LovelaceCard {
|
|||||||
}
|
}
|
||||||
return html`
|
return html`
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
class="${classMap({ "selected-icon": currentMode === mode })}"
|
class=${classMap({ "selected-icon": currentMode === mode })}
|
||||||
.mode="${mode}"
|
.mode=${mode}
|
||||||
.icon="${modeIcons[mode]}"
|
.icon=${modeIcons[mode]}
|
||||||
@click=${this._handleAction}
|
@action=${this._handleAction}
|
||||||
|
.actionHandler=${actionHandler()}
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
></ha-icon-button>
|
></ha-icon-button>
|
||||||
`;
|
`;
|
||||||
@@ -403,6 +460,53 @@ export class HuiThermostatCard extends LitElement implements LovelaceCard {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _handleStepAction(e: MouseEvent): void {
|
||||||
|
const stateObj = this.hass!.states[this._config!.entity] as ClimateEntity;
|
||||||
|
const tempChange = (e.currentTarget as any).tempDiff * this._stepSize;
|
||||||
|
|
||||||
|
if (!Array.isArray(this._setTemp!)) {
|
||||||
|
this.hass!.callService("climate", "set_temperature", {
|
||||||
|
entity_id: this._config!.entity,
|
||||||
|
temperature: (this._setTemp! as number) + tempChange,
|
||||||
|
});
|
||||||
|
} else if (this._lastSetMode === 0) {
|
||||||
|
this.hass!.callService("climate", "set_temperature", {
|
||||||
|
entity_id: this._config!.entity,
|
||||||
|
target_temp_low: this._setTemp![this._lastSetMode] + tempChange,
|
||||||
|
target_temp_high: stateObj.attributes.target_temp_high,
|
||||||
|
});
|
||||||
|
} else if (this._lastSetMode === 1) {
|
||||||
|
this.hass!.callService("climate", "set_temperature", {
|
||||||
|
entity_id: this._config!.entity,
|
||||||
|
target_temp_low: stateObj.attributes.target_temp_low,
|
||||||
|
target_temp_high: this._setTemp![this._lastSetMode] + tempChange,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _attachObserver(): Promise<void> {
|
||||||
|
if (!this._resizeObserver) {
|
||||||
|
await installResizeObserver();
|
||||||
|
this._resizeObserver = new ResizeObserver(
|
||||||
|
debounce(() => this._measureCard(), 250, false)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const card = this.shadowRoot!.querySelector("ha-card");
|
||||||
|
// If we show an error or warning there is no ha-card
|
||||||
|
if (!card) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._resizeObserver.observe(card);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _measureCard() {
|
||||||
|
if (!this.isConnected) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._narrow = this.offsetWidth < 174;
|
||||||
|
}
|
||||||
|
|
||||||
static get styles(): CSSResult {
|
static get styles(): CSSResult {
|
||||||
return css`
|
return css`
|
||||||
:host {
|
:host {
|
||||||
@@ -531,14 +635,29 @@ export class HuiThermostatCard extends LitElement implements LovelaceCard {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#info {
|
#info {
|
||||||
display: flex-vertical;
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
margin-top: -60px;
|
margin-top: -55px;
|
||||||
font-size: var(--name-font-size);
|
font-size: var(--name-font-size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.narrow #info {
|
||||||
|
margin-top: -40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#modes {
|
||||||
|
--mdc-icon-button-size: 32px;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: space-around;
|
||||||
|
width: 100%;
|
||||||
|
padding-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
#modes > * {
|
#modes > * {
|
||||||
color: var(--disabled-text-color);
|
color: var(--disabled-text-color);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
@@ -552,6 +671,36 @@ export class HuiThermostatCard extends LitElement implements LovelaceCard {
|
|||||||
text {
|
text {
|
||||||
fill: var(--primary-text-color);
|
fill: var(--primary-text-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.step-icons {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-around;
|
||||||
|
max-width: 250px;
|
||||||
|
min-width: 100px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step-icon {
|
||||||
|
--mdc-icon-button-size: 28px;
|
||||||
|
--mdc-icon-size: 18px;
|
||||||
|
border-radius: 50%;
|
||||||
|
border: 1px solid;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step-icon.disabled {
|
||||||
|
border-color: var(--disabled-text-color);
|
||||||
|
color: var(--disabled-text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.minus {
|
||||||
|
border-color: var(--cool-color);
|
||||||
|
color: var(--cool-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.plus {
|
||||||
|
border-color: var(--heat-color);
|
||||||
|
color: var(--heat-color);
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user