1
0
mirror of https://github.com/home-assistant/frontend.git synced 2026-04-02 00:27:49 +01:00

Refactor climate HVAC mode handling to be data-driven (#30356)

Refactor climate HVAC mode handling in state-history-chart-line to be data-driven

Replace repetitive per-mode detection, dataset creation, and series population
blocks with a single CLIMATE_MODE_CONFIGS array and loops over active modes.
This commit is contained in:
Petar Petrov
2026-03-28 10:15:45 +01:00
committed by GitHub
parent b4a07f504c
commit df6578dfdf

View File

@@ -28,6 +28,13 @@ const safeParseFloat = (value) => {
return isFinite(parsed) ? parsed : null;
};
const CLIMATE_MODE_CONFIGS = [
{ mode: "heat", action: "heating", cssVar: "--state-climate-heat-color" },
{ mode: "cool", action: "cooling", cssVar: "--state-climate-cool-color" },
{ mode: "dry", action: "drying", cssVar: "--state-climate-dry-color" },
{ mode: "fan_only", action: "fan", cssVar: "--state-climate-fan_only-color" },
] as const;
@customElement("state-history-chart-line")
export class StateHistoryChartLine extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@@ -429,39 +436,18 @@ export class StateHistoryChartLine extends LitElement {
(entityState) => entityState.attributes?.hvac_action
);
const isHeating =
domain === "climate" && hasHvacAction
? (entityState: LineChartState) =>
CLIMATE_HVAC_ACTION_TO_MODE[
entityState.attributes?.hvac_action
] === "heat"
: (entityState: LineChartState) => entityState.state === "heat";
const isCooling =
domain === "climate" && hasHvacAction
? (entityState: LineChartState) =>
CLIMATE_HVAC_ACTION_TO_MODE[
entityState.attributes?.hvac_action
] === "cool"
: (entityState: LineChartState) => entityState.state === "cool";
const isDrying =
domain === "climate" && hasHvacAction
? (entityState: LineChartState) =>
CLIMATE_HVAC_ACTION_TO_MODE[
entityState.attributes?.hvac_action
] === "dry"
: (entityState: LineChartState) => entityState.state === "dry";
const isFanOnly =
domain === "climate" && hasHvacAction
? (entityState: LineChartState) =>
CLIMATE_HVAC_ACTION_TO_MODE[
entityState.attributes?.hvac_action
] === "fan_only"
: (entityState: LineChartState) => entityState.state === "fan_only";
const hasHeat = states.states.some(isHeating);
const hasCool = states.states.some(isCooling);
const hasDry = states.states.some(isDrying);
const hasFan = states.states.some(isFanOnly);
const activeModes = CLIMATE_MODE_CONFIGS.map(
({ mode, action, cssVar }) => {
const isActive =
domain === "climate" && hasHvacAction
? (entityState: LineChartState) =>
CLIMATE_HVAC_ACTION_TO_MODE[
entityState.attributes?.hvac_action
] === mode
: (entityState: LineChartState) => entityState.state === mode;
return { action, cssVar, isActive };
}
).filter(({ isActive }) => states.states.some(isActive));
// We differentiate between thermostats that have a target temperature
// range versus ones that have just a target temperature
@@ -482,61 +468,19 @@ export class StateHistoryChartLine extends LitElement {
"component.climate.entity_component._.state_attributes.current_temperature.name"
)
);
if (hasHeat) {
for (const { action, cssVar } of activeModes) {
addDataSet(
states.entity_id + "-heating",
`${states.entity_id}-${action}`,
this.showNames
? this.hass.localize("ui.card.climate.heating", { name: name })
? this.hass.localize(`ui.card.climate.${action}`, {
name: name,
})
: this.hass.localize(
"component.climate.entity_component._.state_attributes.hvac_action.state.heating"
`component.climate.entity_component._.state_attributes.hvac_action.state.${action}`
),
computedStyles.getPropertyValue("--state-climate-heat-color"),
computedStyles.getPropertyValue(cssVar),
true
);
// The "heating" series uses steppedArea to shade the area below the current
// temperature when the thermostat is calling for heat.
}
if (hasCool) {
addDataSet(
states.entity_id + "-cooling",
this.showNames
? this.hass.localize("ui.card.climate.cooling", { name: name })
: this.hass.localize(
"component.climate.entity_component._.state_attributes.hvac_action.state.cooling"
),
computedStyles.getPropertyValue("--state-climate-cool-color"),
true
);
// The "cooling" series uses steppedArea to shade the area below the current
// temperature when the thermostat is calling for heat.
}
if (hasDry) {
addDataSet(
states.entity_id + "-drying",
this.showNames
? this.hass.localize("ui.card.climate.drying", { name: name })
: this.hass.localize(
"component.climate.entity_component._.state_attributes.hvac_action.state.drying"
),
computedStyles.getPropertyValue("--state-climate-dry-color"),
true
);
// The "drying" series uses steppedArea to shade the area below the current
// temperature when the climate entity is in dry mode.
}
if (hasFan) {
addDataSet(
states.entity_id + "-fan",
this.showNames
? this.hass.localize("ui.card.climate.fan", { name: name })
: this.hass.localize(
"component.climate.entity_component._.state_attributes.hvac_action.state.fan"
),
computedStyles.getPropertyValue("--state-climate-fan_only-color"),
true
);
// The "fan" series uses steppedArea to shade the area below the current
// temperature when the climate entity is in fan_only mode.
}
if (hasTargetRange) {
@@ -584,17 +528,8 @@ export class StateHistoryChartLine extends LitElement {
entityState.attributes.current_temperature
);
const series = [curTemp];
if (hasHeat) {
series.push(isHeating(entityState) ? curTemp : null);
}
if (hasCool) {
series.push(isCooling(entityState) ? curTemp : null);
}
if (hasDry) {
series.push(isDrying(entityState) ? curTemp : null);
}
if (hasFan) {
series.push(isFanOnly(entityState) ? curTemp : null);
for (const { isActive } of activeModes) {
series.push(isActive(entityState) ? curTemp : null);
}
if (hasTargetRange) {
const targetHigh = safeParseFloat(