mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-31 21:17:47 +00:00
First commit
This commit is contained in:
parent
c26a59d805
commit
b2a4fb7cee
@ -4,6 +4,8 @@ import {
|
|||||||
mdiDotsVertical,
|
mdiDotsVertical,
|
||||||
mdiFan,
|
mdiFan,
|
||||||
mdiFire,
|
mdiFire,
|
||||||
|
mdiMinus,
|
||||||
|
mdiPlus,
|
||||||
mdiPower,
|
mdiPower,
|
||||||
mdiSnowflake,
|
mdiSnowflake,
|
||||||
mdiWaterPercent,
|
mdiWaterPercent,
|
||||||
@ -16,10 +18,9 @@ import {
|
|||||||
html,
|
html,
|
||||||
LitElement,
|
LitElement,
|
||||||
PropertyValues,
|
PropertyValues,
|
||||||
svg,
|
|
||||||
TemplateResult,
|
TemplateResult,
|
||||||
} from "lit";
|
} from "lit";
|
||||||
import { customElement, property, state, query } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { classMap } from "lit/directives/class-map";
|
import { classMap } from "lit/directives/class-map";
|
||||||
import { UNIT_F } from "../../../common/const";
|
import { UNIT_F } from "../../../common/const";
|
||||||
import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element";
|
import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element";
|
||||||
@ -27,11 +28,9 @@ import { fireEvent } from "../../../common/dom/fire_event";
|
|||||||
import { computeStateName } from "../../../common/entity/compute_state_name";
|
import { computeStateName } from "../../../common/entity/compute_state_name";
|
||||||
import { formatNumber } from "../../../common/number/format_number";
|
import { formatNumber } from "../../../common/number/format_number";
|
||||||
import "../../../components/ha-card";
|
import "../../../components/ha-card";
|
||||||
import type { HaCard } from "../../../components/ha-card";
|
|
||||||
import "../../../components/ha-icon-button";
|
import "../../../components/ha-icon-button";
|
||||||
import {
|
import {
|
||||||
ClimateEntity,
|
ClimateEntity,
|
||||||
CLIMATE_PRESET_NONE,
|
|
||||||
compareClimateHvacModes,
|
compareClimateHvacModes,
|
||||||
HvacMode,
|
HvacMode,
|
||||||
} from "../../../data/climate";
|
} from "../../../data/climate";
|
||||||
@ -84,8 +83,6 @@ export class HuiThermostatCard extends LitElement implements LovelaceCard {
|
|||||||
|
|
||||||
@state() private _setTemp?: number | number[];
|
@state() private _setTemp?: number | number[];
|
||||||
|
|
||||||
@query("ha-card") private _card?: HaCard;
|
|
||||||
|
|
||||||
public getCardSize(): number {
|
public getCardSize(): number {
|
||||||
return 7;
|
return 7;
|
||||||
}
|
}
|
||||||
@ -116,125 +113,97 @@ export class HuiThermostatCard extends LitElement implements LovelaceCard {
|
|||||||
const name =
|
const name =
|
||||||
this._config!.name ||
|
this._config!.name ||
|
||||||
computeStateName(this.hass!.states[this._config!.entity]);
|
computeStateName(this.hass!.states[this._config!.entity]);
|
||||||
const targetTemp =
|
// const targetTemp =
|
||||||
stateObj.attributes.temperature !== null &&
|
// stateObj.attributes.temperature !== null &&
|
||||||
Number.isFinite(Number(stateObj.attributes.temperature))
|
// Number.isFinite(Number(stateObj.attributes.temperature))
|
||||||
? stateObj.attributes.temperature
|
// ? stateObj.attributes.temperature
|
||||||
: stateObj.attributes.min_temp;
|
// : stateObj.attributes.min_temp;
|
||||||
|
|
||||||
const slider =
|
// const currentTemperature = html`
|
||||||
stateObj.state === UNAVAILABLE
|
// <span viewBox="0 0 40 20">
|
||||||
? html` <round-slider disabled="true"></round-slider> `
|
// <span
|
||||||
: html`
|
// x="50%"
|
||||||
<round-slider
|
// dx="1"
|
||||||
.value=${targetTemp}
|
// y="60%"
|
||||||
.low=${stateObj.attributes.target_temp_low}
|
// text-anchor="middle"
|
||||||
.high=${stateObj.attributes.target_temp_high}
|
// style="font-size: 13px;"
|
||||||
.min=${stateObj.attributes.min_temp}
|
// >
|
||||||
.max=${stateObj.attributes.max_temp}
|
// ${stateObj.attributes.current_temperature !== null &&
|
||||||
.step=${this._stepSize}
|
// !isNaN(stateObj.attributes.current_temperature)
|
||||||
@value-changing=${this._dragEvent}
|
// ? html`${formatNumber(
|
||||||
@value-changed=${this._setTemperature}
|
// stateObj.attributes.current_temperature,
|
||||||
></round-slider>
|
// this.hass.locale
|
||||||
`;
|
// )}
|
||||||
|
// <span dx="-3" dy="-6.5" style="font-size: 4px;">
|
||||||
|
// ${this.hass.config.unit_system.temperature}
|
||||||
|
// </span>`
|
||||||
|
// : ""}
|
||||||
|
// </span>
|
||||||
|
// </span>
|
||||||
|
// `;
|
||||||
|
|
||||||
const currentTemperature = svg`
|
// const setValues = html`
|
||||||
<svg viewBox="0 0 40 20">
|
// <html id="set-values">
|
||||||
<text
|
// <span class="set-value">
|
||||||
x="50%"
|
// ${stateObj.state === UNAVAILABLE
|
||||||
dx="1"
|
// ? this.hass.localize("state.default.unavailable")
|
||||||
y="60%"
|
// : this._setTemp === undefined || this._setTemp === null
|
||||||
text-anchor="middle"
|
// ? ""
|
||||||
style="font-size: 13px;"
|
// : Array.isArray(this._setTemp)
|
||||||
>
|
// ? this._stepSize === 1
|
||||||
${
|
// ? html`
|
||||||
stateObj.attributes.current_temperature !== null &&
|
// ${formatNumber(this._setTemp[0], this.hass.locale, {
|
||||||
!isNaN(stateObj.attributes.current_temperature)
|
// maximumFractionDigits: 0,
|
||||||
? svg`${formatNumber(
|
// })}
|
||||||
stateObj.attributes.current_temperature,
|
// -
|
||||||
this.hass.locale
|
// ${formatNumber(this._setTemp[1], this.hass.locale, {
|
||||||
)}
|
// maximumFractionDigits: 0,
|
||||||
<tspan dx="-3" dy="-6.5" style="font-size: 4px;">
|
// })}
|
||||||
${this.hass.config.unit_system.temperature}
|
// `
|
||||||
</tspan>`
|
// : html`
|
||||||
: ""
|
// ${formatNumber(this._setTemp[0], this.hass.locale, {
|
||||||
}
|
// minimumFractionDigits: 1,
|
||||||
</text>
|
// maximumFractionDigits: 1,
|
||||||
</svg>
|
// })}
|
||||||
`;
|
// -
|
||||||
|
// ${formatNumber(this._setTemp[1], this.hass.locale, {
|
||||||
const setValues = svg`
|
// minimumFractionDigits: 1,
|
||||||
<svg id="set-values">
|
// maximumFractionDigits: 1,
|
||||||
<g>
|
// })}
|
||||||
<text text-anchor="middle" class="set-value">
|
// `
|
||||||
${
|
// : this._stepSize === 1
|
||||||
stateObj.state === UNAVAILABLE
|
// ? html`
|
||||||
? this.hass.localize("state.default.unavailable")
|
// ${formatNumber(this._setTemp, this.hass.locale, {
|
||||||
: this._setTemp === undefined || this._setTemp === null
|
// maximumFractionDigits: 0,
|
||||||
? ""
|
// })}
|
||||||
: Array.isArray(this._setTemp)
|
// `
|
||||||
? this._stepSize === 1
|
// : html`
|
||||||
? svg`
|
// ${formatNumber(this._setTemp, this.hass.locale, {
|
||||||
${formatNumber(this._setTemp[0], this.hass.locale, {
|
// minimumFractionDigits: 1,
|
||||||
maximumFractionDigits: 0,
|
// maximumFractionDigits: 1,
|
||||||
})} -
|
// })}
|
||||||
${formatNumber(this._setTemp[1], this.hass.locale, {
|
// `}
|
||||||
maximumFractionDigits: 0,
|
// </span>
|
||||||
})}
|
// <span>
|
||||||
`
|
// ${stateObj.attributes.hvac_action
|
||||||
: svg`
|
// ? this.hass!.localize(
|
||||||
${formatNumber(this._setTemp[0], this.hass.locale, {
|
// `state_attributes.climate.hvac_action.${stateObj.attributes.hvac_action}`
|
||||||
minimumFractionDigits: 1,
|
// )
|
||||||
maximumFractionDigits: 1,
|
// : this.hass!.localize(
|
||||||
})} -
|
// `component.climate.state._.${stateObj.state}`
|
||||||
${formatNumber(this._setTemp[1], this.hass.locale, {
|
// )}
|
||||||
minimumFractionDigits: 1,
|
// ${stateObj.attributes.preset_mode &&
|
||||||
maximumFractionDigits: 1,
|
// stateObj.attributes.preset_mode !== CLIMATE_PRESET_NONE
|
||||||
})}
|
// ? html`
|
||||||
`
|
// -
|
||||||
: this._stepSize === 1
|
// ${this.hass!.localize(
|
||||||
? svg`
|
// `state_attributes.climate.preset_mode.${stateObj.attributes.preset_mode}`
|
||||||
${formatNumber(this._setTemp, this.hass.locale, {
|
// ) || stateObj.attributes.preset_mode}
|
||||||
maximumFractionDigits: 0,
|
// `
|
||||||
})}
|
// : ""}
|
||||||
`
|
// </span>
|
||||||
: svg`
|
// </html>
|
||||||
${formatNumber(this._setTemp, this.hass.locale, {
|
// `;
|
||||||
minimumFractionDigits: 1,
|
|
||||||
maximumFractionDigits: 1,
|
|
||||||
})}
|
|
||||||
`
|
|
||||||
}
|
|
||||||
</text>
|
|
||||||
<text
|
|
||||||
dy="22"
|
|
||||||
text-anchor="middle"
|
|
||||||
id="set-mode"
|
|
||||||
>
|
|
||||||
${
|
|
||||||
stateObj.attributes.hvac_action
|
|
||||||
? this.hass!.localize(
|
|
||||||
`state_attributes.climate.hvac_action.${stateObj.attributes.hvac_action}`
|
|
||||||
)
|
|
||||||
: this.hass!.localize(
|
|
||||||
`component.climate.state._.${stateObj.state}`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
${
|
|
||||||
stateObj.attributes.preset_mode &&
|
|
||||||
stateObj.attributes.preset_mode !== CLIMATE_PRESET_NONE
|
|
||||||
? html`
|
|
||||||
-
|
|
||||||
${this.hass!.localize(
|
|
||||||
`state_attributes.climate.preset_mode.${stateObj.attributes.preset_mode}`
|
|
||||||
) || stateObj.attributes.preset_mode}
|
|
||||||
`
|
|
||||||
: ""
|
|
||||||
}
|
|
||||||
</text>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
`;
|
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<ha-card
|
<ha-card
|
||||||
@ -251,25 +220,95 @@ export class HuiThermostatCard extends LitElement implements LovelaceCard {
|
|||||||
@click=${this._handleMoreInfo}
|
@click=${this._handleMoreInfo}
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
></ha-icon-button>
|
></ha-icon-button>
|
||||||
|
<div class="current-controls">
|
||||||
|
<div class="current">
|
||||||
|
${stateObj.attributes.current_temperature !== null &&
|
||||||
|
!isNaN(stateObj.attributes.current_temperature)
|
||||||
|
? html`
|
||||||
|
<span class="current-temp">
|
||||||
|
${formatNumber(
|
||||||
|
stateObj.attributes.current_temperature,
|
||||||
|
this.hass.locale
|
||||||
|
)}
|
||||||
|
<span class="current-temp-unit">
|
||||||
|
${this.hass.config.unit_system.temperature}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
|
<span class="current-action">
|
||||||
|
${stateObj.attributes.hvac_action
|
||||||
|
? this.hass!.localize(
|
||||||
|
`state_attributes.climate.hvac_action.${stateObj.attributes.hvac_action}`
|
||||||
|
)
|
||||||
|
: this.hass!.localize(
|
||||||
|
`component.climate.state._.${stateObj.state}`
|
||||||
|
)}
|
||||||
|
to
|
||||||
|
${stateObj.state === UNAVAILABLE
|
||||||
|
? this.hass.localize("state.default.unavailable")
|
||||||
|
: this._setTemp === undefined || this._setTemp === null
|
||||||
|
? ""
|
||||||
|
: Array.isArray(this._setTemp)
|
||||||
|
? this._stepSize === 1
|
||||||
|
? html`
|
||||||
|
${formatNumber(this._setTemp[0], this.hass.locale, {
|
||||||
|
maximumFractionDigits: 0,
|
||||||
|
})}
|
||||||
|
-
|
||||||
|
${formatNumber(this._setTemp[1], this.hass.locale, {
|
||||||
|
maximumFractionDigits: 0,
|
||||||
|
})}
|
||||||
|
`
|
||||||
|
: html`
|
||||||
|
${formatNumber(this._setTemp[0], this.hass.locale, {
|
||||||
|
minimumFractionDigits: 1,
|
||||||
|
maximumFractionDigits: 1,
|
||||||
|
})}
|
||||||
|
-
|
||||||
|
${formatNumber(this._setTemp[1], this.hass.locale, {
|
||||||
|
minimumFractionDigits: 1,
|
||||||
|
maximumFractionDigits: 1,
|
||||||
|
})}
|
||||||
|
`
|
||||||
|
: this._stepSize === 1
|
||||||
|
? html`
|
||||||
|
${formatNumber(this._setTemp, this.hass.locale, {
|
||||||
|
maximumFractionDigits: 0,
|
||||||
|
})}
|
||||||
|
`
|
||||||
|
: html`
|
||||||
|
${formatNumber(this._setTemp, this.hass.locale, {
|
||||||
|
minimumFractionDigits: 1,
|
||||||
|
maximumFractionDigits: 1,
|
||||||
|
})}
|
||||||
|
`}</span
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<div class="controls">
|
||||||
|
<ha-icon-button
|
||||||
|
@click=${this._handleAction}
|
||||||
|
tabindex="0"
|
||||||
|
.path=${mdiPlus}
|
||||||
|
>
|
||||||
|
</ha-icon-button>
|
||||||
|
<ha-icon-button
|
||||||
|
@click=${this._handleAction}
|
||||||
|
tabindex="0"
|
||||||
|
.path=${mdiMinus}
|
||||||
|
>
|
||||||
|
</ha-icon-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="content">
|
<div id="info" .title=${name}>
|
||||||
<div id="controls">
|
<div id="modes">
|
||||||
<div id="slider">
|
${(stateObj.attributes.hvac_modes || [])
|
||||||
${slider}
|
.concat()
|
||||||
<div id="slider-center">
|
.sort(compareClimateHvacModes)
|
||||||
<div id="temperature">${currentTemperature} ${setValues}</div>
|
.map((modeItem) => this._renderIcon(modeItem, mode))}
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div id="info" .title=${name}>
|
|
||||||
<div id="modes">
|
|
||||||
${(stateObj.attributes.hvac_modes || [])
|
|
||||||
.concat()
|
|
||||||
.sort(compareClimateHvacModes)
|
|
||||||
.map((modeItem) => this._renderIcon(modeItem, mode))}
|
|
||||||
</div>
|
|
||||||
${name}
|
|
||||||
</div>
|
</div>
|
||||||
|
${name}
|
||||||
</div>
|
</div>
|
||||||
</ha-card>
|
</ha-card>
|
||||||
`;
|
`;
|
||||||
@ -303,15 +342,6 @@ export class HuiThermostatCard extends LitElement implements LovelaceCard {
|
|||||||
) {
|
) {
|
||||||
applyThemesOnElement(this, this.hass.themes, this._config.theme);
|
applyThemesOnElement(this, this.hass.themes, this._config.theme);
|
||||||
}
|
}
|
||||||
|
|
||||||
const stateObj = this.hass.states[this._config.entity];
|
|
||||||
if (!stateObj) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!oldHass || oldHass.states[this._config.entity] !== stateObj) {
|
|
||||||
this._rescale_svg();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public willUpdate(changedProps: PropertyValues) {
|
public willUpdate(changedProps: PropertyValues) {
|
||||||
@ -331,27 +361,6 @@ export class HuiThermostatCard extends LitElement implements LovelaceCard {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _rescale_svg() {
|
|
||||||
// Set the viewbox of the SVG containing the set temperature to perfectly
|
|
||||||
// fit the text
|
|
||||||
// That way it will auto-scale correctly
|
|
||||||
// This is not done to the SVG containing the current temperature, because
|
|
||||||
// it should not be centered on the text, but only on the value
|
|
||||||
const card = this._card;
|
|
||||||
if (card) {
|
|
||||||
card.updateComplete.then(() => {
|
|
||||||
const svgRoot = this.shadowRoot!.querySelector("#set-values")!;
|
|
||||||
const box = svgRoot.querySelector("g")!.getBBox()!;
|
|
||||||
svgRoot.setAttribute(
|
|
||||||
"viewBox",
|
|
||||||
`${box.x} ${box!.y} ${box.width} ${box.height}`
|
|
||||||
);
|
|
||||||
svgRoot.setAttribute("width", `${box.width}`);
|
|
||||||
svgRoot.setAttribute("height", `${box.height}`);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private get _stepSize(): number {
|
private get _stepSize(): number {
|
||||||
const stateObj = this.hass!.states[this._config!.entity] as ClimateEntity;
|
const stateObj = this.hass!.states[this._config!.entity] as ClimateEntity;
|
||||||
|
|
||||||
@ -381,18 +390,6 @@ export class HuiThermostatCard extends LitElement implements LovelaceCard {
|
|||||||
return stateObj.attributes.temperature;
|
return stateObj.attributes.temperature;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _dragEvent(e): void {
|
|
||||||
const stateObj = this.hass!.states[this._config!.entity] as ClimateEntity;
|
|
||||||
|
|
||||||
if (e.detail.low) {
|
|
||||||
this._setTemp = [e.detail.low, stateObj.attributes.target_temp_high];
|
|
||||||
} else if (e.detail.high) {
|
|
||||||
this._setTemp = [stateObj.attributes.target_temp_low, e.detail.high];
|
|
||||||
} else {
|
|
||||||
this._setTemp = e.detail.value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private _setTemperature(e): void {
|
private _setTemperature(e): void {
|
||||||
const stateObj = this.hass!.states[this._config!.entity] as ClimateEntity;
|
const stateObj = this.hass!.states[this._config!.entity] as ClimateEntity;
|
||||||
|
|
||||||
@ -508,47 +505,49 @@ export class HuiThermostatCard extends LitElement implements LovelaceCard {
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
#controls {
|
.current-controls {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.current {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.current-temp {
|
||||||
|
font-size: 64px;
|
||||||
|
font-weight: 300;
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
line-height: 0.8;
|
||||||
|
padding-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.current-temp-unit {
|
||||||
|
font-size: 22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.current-action {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.controls {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
flex-direction: column;
|
||||||
|
min-height: 100px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#slider {
|
.controls ha-icon-button {
|
||||||
height: 100%;
|
border: 1px solid var(--divider-color);
|
||||||
width: 100%;
|
border-radius: 50%;
|
||||||
position: relative;
|
--mdc-icon-button-size: 36px;
|
||||||
max-width: 250px;
|
|
||||||
min-width: 100px;
|
|
||||||
}
|
|
||||||
|
|
||||||
round-slider {
|
|
||||||
--round-slider-path-color: var(--slider-track-color);
|
|
||||||
--round-slider-bar-color: var(--mode-color);
|
|
||||||
padding-bottom: 10%;
|
|
||||||
}
|
|
||||||
|
|
||||||
#slider-center {
|
|
||||||
position: absolute;
|
|
||||||
width: calc(100% - 40px);
|
|
||||||
height: calc(100% - 40px);
|
|
||||||
box-sizing: border-box;
|
|
||||||
border-radius: 100%;
|
|
||||||
left: 20px;
|
|
||||||
top: 20px;
|
|
||||||
text-align: center;
|
|
||||||
overflow-wrap: break-word;
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
#temperature {
|
|
||||||
position: absolute;
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
width: 100%;
|
|
||||||
height: 50%;
|
|
||||||
top: 45%;
|
|
||||||
left: 50%;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#set-values {
|
#set-values {
|
||||||
@ -566,8 +565,7 @@ export class HuiThermostatCard extends LitElement implements LovelaceCard {
|
|||||||
display: flex-vertical;
|
display: flex-vertical;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding: 16px;
|
padding: 0 16px 16px 16px;
|
||||||
margin-top: -60px;
|
|
||||||
font-size: var(--name-font-size);
|
font-size: var(--name-font-size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user