First commit

This commit is contained in:
Zack Arnett 2021-11-10 17:30:06 -06:00
parent c26a59d805
commit b2a4fb7cee

View File

@ -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);
} }