diff --git a/package.json b/package.json index 60688e33b7..25616cee3e 100644 --- a/package.json +++ b/package.json @@ -99,6 +99,7 @@ "@webcomponents/webcomponentsjs": "2.8.0", "app-datepicker": "5.1.1", "chart.js": "4.4.1", + "color-name": "~1.1.4", "comlink": "4.4.1", "core-js": "3.36.0", "cropperjs": "1.6.1", @@ -170,6 +171,7 @@ "@types/babel__plugin-transform-runtime": "7.9.5", "@types/chromecast-caf-receiver": "6.0.13", "@types/chromecast-caf-sender": "1.0.8", + "@types/color-name": "^1", "@types/glob": "8.1.0", "@types/html-minifier-terser": "7.0.2", "@types/js-yaml": "4.0.9", diff --git a/src/panels/lovelace/cards/energy/common/color.ts b/src/panels/lovelace/cards/energy/common/color.ts new file mode 100644 index 0000000000..f9f02f3674 --- /dev/null +++ b/src/panels/lovelace/cards/energy/common/color.ts @@ -0,0 +1,64 @@ +import colors from "color-name"; +import { + hex2rgb, + lab2rgb, + rgb2hex, + rgb2lab, +} from "../../../../../common/color/convert-color"; +import { labBrighten, labDarken } from "../../../../../common/color/lab"; + +export function getEnergyColor( + computedStyles: CSSStyleDeclaration, + darkMode: boolean, + background: boolean, + compare: boolean, + propertyName: string, + idx?: number +): string { + const themeIdxColor = computedStyles + .getPropertyValue(propertyName + "-" + idx) + .trim(); + + const themeColor = + themeIdxColor.length > 0 + ? themeIdxColor + : computedStyles.getPropertyValue(propertyName).trim(); + + let hexColor; + if (themeColor.startsWith("#")) { + hexColor = themeColor; + } else { + const rgbFromColorName = colors[themeColor]; + if (!rgbFromColorName) { + // We have a named color, and there's nothing in the table, + // so nothing further we can do with it. + // Compare/border/background color will all be the same. + return themeColor; + } + hexColor = rgb2hex(rgbFromColorName); + } + + if (themeIdxColor.length === 0 && idx) { + // Brighten or darken the color based on set position. + // Skip if theme already provides a color for this set. + + hexColor = rgb2hex( + lab2rgb( + darkMode + ? labBrighten(rgb2lab(hex2rgb(hexColor)), idx) + : labDarken(rgb2lab(hex2rgb(hexColor)), idx) + ) + ); + } + + if (compare) { + if (background) { + hexColor += "32"; + } else { + hexColor += "7F"; + } + } else if (background) { + hexColor += "7F"; + } + return hexColor; +} diff --git a/src/panels/lovelace/cards/energy/hui-energy-gas-graph-card.ts b/src/panels/lovelace/cards/energy/hui-energy-gas-graph-card.ts index e55383015b..baf3b2a7a7 100644 --- a/src/panels/lovelace/cards/energy/hui-energy-gas-graph-card.ts +++ b/src/panels/lovelace/cards/energy/hui-energy-gas-graph-card.ts @@ -17,13 +17,7 @@ import { import { customElement, property, state } from "lit/decorators"; import { classMap } from "lit/directives/class-map"; import memoizeOne from "memoize-one"; -import { - hex2rgb, - lab2rgb, - rgb2hex, - rgb2lab, -} from "../../../../common/color/convert-color"; -import { labBrighten, labDarken } from "../../../../common/color/lab"; +import { getEnergyColor } from "./common/color"; import { formatNumber } from "../../../../common/number/format_number"; import "../../../../components/chart/ha-chart-base"; import "../../../../components/ha-card"; @@ -204,16 +198,12 @@ export class HuiEnergyGasGraphCard const datasets: ChartDataset<"bar", ScatterDataPoint[]>[] = []; const computedStyles = getComputedStyle(this); - const gasColor = computedStyles - .getPropertyValue("--energy-gas-color") - .trim(); datasets.push( ...this._processDataSet( energyData.stats, energyData.statsMetadata, gasSources, - gasColor, computedStyles ) ); @@ -235,7 +225,6 @@ export class HuiEnergyGasGraphCard energyData.statsCompare, energyData.statsMetadata, gasSources, - gasColor, computedStyles, true ) @@ -257,28 +246,12 @@ export class HuiEnergyGasGraphCard statistics: Statistics, statisticsMetaData: Record, gasSources: GasSourceTypeEnergyPreference[], - gasColor: string, computedStyles: CSSStyleDeclaration, compare = false ) { const data: ChartDataset<"bar", ScatterDataPoint[]>[] = []; gasSources.forEach((source, idx) => { - let borderColor = computedStyles - .getPropertyValue("--energy-gas-color-" + idx) - .trim(); - if (borderColor.length === 0) { - const modifiedColor = - idx > 0 - ? this.hass.themes.darkMode - ? labBrighten(rgb2lab(hex2rgb(gasColor)), idx) - : labDarken(rgb2lab(hex2rgb(gasColor)), idx) - : undefined; - borderColor = modifiedColor - ? rgb2hex(lab2rgb(modifiedColor)) - : gasColor; - } - let prevStart: number | null = null; const gasConsumptionData: ScatterDataPoint[] = []; @@ -317,8 +290,22 @@ export class HuiEnergyGasGraphCard source.stat_energy_from, statisticsMetaData[source.stat_energy_from] ), - borderColor: compare ? borderColor + "7F" : borderColor, - backgroundColor: compare ? borderColor + "32" : borderColor + "7F", + borderColor: getEnergyColor( + computedStyles, + this.hass.themes.darkMode, + false, + compare, + "--energy-gas-color", + idx + ), + backgroundColor: getEnergyColor( + computedStyles, + this.hass.themes.darkMode, + true, + compare, + "--energy-gas-color", + idx + ), data: gasConsumptionData, order: 1, stack: "gas", diff --git a/src/panels/lovelace/cards/energy/hui-energy-solar-graph-card.ts b/src/panels/lovelace/cards/energy/hui-energy-solar-graph-card.ts index d5f3e767d0..40d91584dc 100644 --- a/src/panels/lovelace/cards/energy/hui-energy-solar-graph-card.ts +++ b/src/panels/lovelace/cards/energy/hui-energy-solar-graph-card.ts @@ -22,13 +22,7 @@ import { import { customElement, property, state } from "lit/decorators"; import { classMap } from "lit/directives/class-map"; import memoizeOne from "memoize-one"; -import { - hex2rgb, - lab2rgb, - rgb2hex, - rgb2lab, -} from "../../../../common/color/convert-color"; -import { labBrighten, labDarken } from "../../../../common/color/lab"; +import { getEnergyColor } from "./common/color"; import { formatNumber } from "../../../../common/number/format_number"; import "../../../../components/chart/ha-chart-base"; import "../../../../components/ha-card"; @@ -226,16 +220,12 @@ export class HuiEnergySolarGraphCard const datasets: ChartDataset<"bar" | "line">[] = []; const computedStyles = getComputedStyle(this); - const solarColor = computedStyles - .getPropertyValue("--energy-solar-color") - .trim(); datasets.push( ...this._processDataSet( energyData.stats, energyData.statsMetadata, solarSources, - solarColor, computedStyles ) ); @@ -257,7 +247,6 @@ export class HuiEnergySolarGraphCard energyData.statsCompare, energyData.statsMetadata, solarSources, - solarColor, computedStyles, true ) @@ -292,28 +281,12 @@ export class HuiEnergySolarGraphCard statistics: Statistics, statisticsMetaData: Record, solarSources: SolarSourceTypeEnergyPreference[], - solarColor: string, computedStyles: CSSStyleDeclaration, compare = false ) { const data: ChartDataset<"bar", ScatterDataPoint[]>[] = []; solarSources.forEach((source, idx) => { - let borderColor = computedStyles - .getPropertyValue("--energy-solar-color-" + idx) - .trim(); - if (borderColor.length === 0) { - const modifiedColor = - idx > 0 - ? this.hass.themes.darkMode - ? labBrighten(rgb2lab(hex2rgb(solarColor)), idx) - : labDarken(rgb2lab(hex2rgb(solarColor)), idx) - : undefined; - borderColor = modifiedColor - ? rgb2hex(lab2rgb(modifiedColor)) - : solarColor; - } - let prevStart: number | null = null; const solarProductionData: ScatterDataPoint[] = []; @@ -357,8 +330,22 @@ export class HuiEnergySolarGraphCard ), } ), - borderColor: compare ? borderColor + "7F" : borderColor, - backgroundColor: compare ? borderColor + "32" : borderColor + "7F", + borderColor: getEnergyColor( + computedStyles, + this.hass.themes.darkMode, + false, + compare, + "--energy-solar-color", + idx + ), + backgroundColor: getEnergyColor( + computedStyles, + this.hass.themes.darkMode, + true, + compare, + "--energy-solar-color", + idx + ), data: solarProductionData, order: 1, stack: "solar", diff --git a/src/panels/lovelace/cards/energy/hui-energy-sources-table-card.ts b/src/panels/lovelace/cards/energy/hui-energy-sources-table-card.ts index 2be70f203b..4373c30bd1 100644 --- a/src/panels/lovelace/cards/energy/hui-energy-sources-table-card.ts +++ b/src/panels/lovelace/cards/energy/hui-energy-sources-table-card.ts @@ -12,14 +12,8 @@ import { } from "lit"; import { customElement, property, state } from "lit/decorators"; import { styleMap } from "lit/directives/style-map"; -import { - hex2rgb, - lab2rgb, - rgb2hex, - rgb2lab, -} from "../../../../common/color/convert-color"; -import { labBrighten, labDarken } from "../../../../common/color/lab"; import { formatNumber } from "../../../../common/number/format_number"; +import { getEnergyColor } from "./common/color"; import "../../../../components/ha-card"; import { EnergyData, @@ -38,6 +32,16 @@ import { LovelaceCard } from "../../types"; import { EnergySourcesTableCardConfig } from "../types"; import { hasConfigChanged } from "../../common/has-changed"; +const colorPropertyMap = { + grid_return: "--energy-grid-return-color", + grid_consumption: "--energy-grid-consumption-color", + battery_in: "--energy-battery-in-color", + battery_out: "--energy-battery-out-color", + solar: "--energy-solar-color", + gas: "--energy-gas-color", + water: "--energy-water-color", +}; + @customElement("hui-energy-sources-table-card") export class HuiEnergySourcesTableCard extends SubscribeMixin(LitElement) @@ -77,27 +81,6 @@ export class HuiEnergySourcesTableCard ); } - private _getColor( - computedStyles: CSSStyleDeclaration, - propertyName: string, - baseColor: string, - idx: number - ): string { - let color = computedStyles - .getPropertyValue(propertyName + "-" + idx) - .trim(); - if (color.length === 0) { - const modifiedColor = - idx > 0 - ? this.hass.themes.darkMode - ? labBrighten(rgb2lab(hex2rgb(baseColor)), idx) - : labDarken(rgb2lab(hex2rgb(baseColor)), idx) - : undefined; - color = modifiedColor ? rgb2hex(lab2rgb(modifiedColor)) : baseColor; - } - return color; - } - protected render() { if (!this.hass || !this._config) { return nothing; @@ -133,38 +116,7 @@ export class HuiEnergySourcesTableCard const types = energySourcesByType(this._data.prefs); - const colorPropertyMap = { - grid_return: "--energy-grid-return-color", - grid_consumption: "--energy-grid-consumption-color", - battery_in: "--energy-battery-in-color", - battery_out: "--energy-battery-out-color", - solar: "--energy-solar-color", - gas: "--energy-gas-color", - water: "--energy-water-color", - }; - const computedStyles = getComputedStyle(this); - const solarColor = computedStyles - .getPropertyValue(colorPropertyMap.solar) - .trim(); - const batteryFromColor = computedStyles - .getPropertyValue(colorPropertyMap.battery_out) - .trim(); - const batteryToColor = computedStyles - .getPropertyValue(colorPropertyMap.battery_in) - .trim(); - const returnColor = computedStyles - .getPropertyValue(colorPropertyMap.grid_return) - .trim(); - const consumptionColor = computedStyles - .getPropertyValue(colorPropertyMap.grid_consumption) - .trim(); - const gasColor = computedStyles - .getPropertyValue(colorPropertyMap.gas) - .trim(); - const waterColor = computedStyles - .getPropertyValue(colorPropertyMap.water) - .trim(); const showCosts = types.grid?.[0].flow_from.some( @@ -273,20 +225,27 @@ export class HuiEnergySourcesTableCard 0; totalSolarCompare += compareEnergy; - const color = this._getColor( - computedStyles, - colorPropertyMap.solar, - solarColor, - idx - ); - return html`
@@ -371,26 +330,27 @@ export class HuiEnergySourcesTableCard 0; totalBatteryCompare += energyFromCompare - energyToCompare; - const fromColor = this._getColor( - computedStyles, - colorPropertyMap.battery_out, - batteryFromColor, - idx - ); - const toColor = this._getColor( - computedStyles, - colorPropertyMap.battery_in, - batteryToColor, - idx - ); - return html`
@@ -426,8 +386,22 @@ export class HuiEnergySourcesTableCard
@@ -534,20 +508,27 @@ export class HuiEnergySourcesTableCard totalGridCostCompare += costCompare; } - const color = this._getColor( - computedStyles, - colorPropertyMap.grid_consumption, - consumptionColor, - idx - ); - return html`
@@ -638,20 +619,27 @@ export class HuiEnergySourcesTableCard totalGridCostCompare += costCompare; } - const color = this._getColor( - computedStyles, - colorPropertyMap.grid_return, - returnColor, - idx - ); - return html`
@@ -794,20 +782,27 @@ export class HuiEnergySourcesTableCard totalGasCostCompare += costCompare; } - const color = this._getColor( - computedStyles, - colorPropertyMap.gas, - gasColor, - idx - ); - return html`
@@ -945,20 +940,27 @@ export class HuiEnergySourcesTableCard totalWaterCostCompare += costCompare; } - const color = this._getColor( - computedStyles, - colorPropertyMap.water, - waterColor, - idx - ); - return html`
diff --git a/src/panels/lovelace/cards/energy/hui-energy-usage-graph-card.ts b/src/panels/lovelace/cards/energy/hui-energy-usage-graph-card.ts index 5b28a4195b..7b7c45db80 100644 --- a/src/panels/lovelace/cards/energy/hui-energy-usage-graph-card.ts +++ b/src/panels/lovelace/cards/energy/hui-energy-usage-graph-card.ts @@ -17,13 +17,7 @@ import { import { customElement, property, state } from "lit/decorators"; import { classMap } from "lit/directives/class-map"; import memoizeOne from "memoize-one"; -import { - hex2rgb, - lab2rgb, - rgb2hex, - rgb2lab, -} from "../../../../common/color/convert-color"; -import { labBrighten, labDarken } from "../../../../common/color/lab"; +import { getEnergyColor } from "./common/color"; import { formatNumber } from "../../../../common/number/format_number"; import "../../../../components/chart/ha-chart-base"; import "../../../../components/ha-card"; @@ -41,10 +35,14 @@ import { EnergyUsageGraphCardConfig } from "../types"; import { hasConfigChanged } from "../../common/has-changed"; import { getCommonOptions } from "./common/energy-chart-options"; -interface ColorSet { - base: string; - overrides?: Record; -} +const colorPropertyMap = { + to_grid: "--energy-grid-return-color", + to_battery: "--energy-battery-in-color", + from_grid: "--energy-grid-consumption-color", + used_grid: "--energy-grid-consumption-color", + used_solar: "--energy-solar-color", + used_battery: "--energy-battery-out-color", +}; @customElement("hui-energy-usage-graph-card") export class HuiEnergyUsageGraphCard @@ -263,47 +261,9 @@ export class HuiEnergyUsageGraphCard const computedStyles = getComputedStyle(this); - const colorPropertyMap = { - to_grid: "--energy-grid-return-color", - to_battery: "--energy-battery-in-color", - from_grid: "--energy-grid-consumption-color", - used_grid: "--energy-grid-consumption-color", - used_solar: "--energy-solar-color", - used_battery: "--energy-battery-out-color", - }; - - const colors = { - to_grid: { - base: computedStyles.getPropertyValue(colorPropertyMap.to_grid).trim(), - }, - to_battery: { - base: computedStyles - .getPropertyValue(colorPropertyMap.to_battery) - .trim(), - }, - from_grid: { - base: computedStyles - .getPropertyValue(colorPropertyMap.from_grid) - .trim(), - }, - used_grid: { - base: computedStyles - .getPropertyValue(colorPropertyMap.used_grid) - .trim(), - }, - used_solar: { - base: computedStyles - .getPropertyValue(colorPropertyMap.used_solar) - .trim(), - }, - used_battery: { - base: computedStyles - .getPropertyValue(colorPropertyMap.used_battery) - .trim(), - }, - }; - - Object.entries(colorPropertyMap).forEach(([key, colorProp]) => { + const colorIndices: Record> = {}; + Object.keys(colorPropertyMap).forEach((key) => { + colorIndices[key] = {}; if ( key === "used_grid" || key === "used_solar" || @@ -311,15 +271,9 @@ export class HuiEnergyUsageGraphCard ) { return; } - colors[key].overrides = []; if (statIds[key]) { Object.values(statIds[key]).forEach((id, idx) => { - const override = computedStyles - .getPropertyValue(colorProp + "-" + idx) - .trim(); - if (override.length > 0) { - colors[key].overrides[id] = override; - } + colorIndices[key][id as string] = idx; }); } }); @@ -347,7 +301,8 @@ export class HuiEnergyUsageGraphCard energyData.stats, energyData.statsMetadata, statIds, - colors, + colorIndices, + computedStyles, labels, false ) @@ -370,7 +325,8 @@ export class HuiEnergyUsageGraphCard energyData.statsCompare, energyData.statsMetadata, statIds, - colors, + colorIndices, + computedStyles, labels, true ) @@ -392,14 +348,8 @@ export class HuiEnergyUsageGraphCard to_battery?: string[] | undefined; from_battery?: string[] | undefined; }, - colors: { - to_grid: ColorSet; - to_battery: ColorSet; - from_grid: ColorSet; - used_grid: ColorSet; - used_solar: ColorSet; - used_battery: ColorSet; - }, + colorIndices: Record>, + computedStyles: CSSStyleDeclaration, labels: { used_grid: string; used_solar: string; @@ -553,19 +503,6 @@ export class HuiEnergyUsageGraphCard Object.entries(combinedData).forEach(([type, sources]) => { Object.entries(sources).forEach(([statId, source], idx) => { - let borderColor = colors[type].overrides?.[statId]; - if (!borderColor) { - const modifiedColor = - idx > 0 - ? this.hass.themes.darkMode - ? labBrighten(rgb2lab(hex2rgb(colors[type].base)), idx) - : labDarken(rgb2lab(hex2rgb(colors[type].base)), idx) - : undefined; - borderColor = modifiedColor - ? rgb2hex(lab2rgb(modifiedColor)) - : colors[type].base; - } - const points: ScatterDataPoint[] = []; // Process chart data. for (const key of uniqueKeys) { @@ -600,8 +537,22 @@ export class HuiEnergyUsageGraphCard : type === "to_battery" ? Object.keys(combinedData).length : idx + 2, - borderColor: compare ? borderColor + "7F" : borderColor, - backgroundColor: compare ? borderColor + "32" : borderColor + "7F", + borderColor: getEnergyColor( + computedStyles, + this.hass.themes.darkMode, + false, + compare, + colorPropertyMap[type], + colorIndices[type]?.[statId] + ), + backgroundColor: getEnergyColor( + computedStyles, + this.hass.themes.darkMode, + true, + compare, + colorPropertyMap[type], + colorIndices[type]?.[statId] + ), stack: "stack", data: points, xAxisID: compare ? "xAxisCompare" : undefined, diff --git a/src/panels/lovelace/cards/energy/hui-energy-water-graph-card.ts b/src/panels/lovelace/cards/energy/hui-energy-water-graph-card.ts index e05d6fb665..d5011fc9cb 100644 --- a/src/panels/lovelace/cards/energy/hui-energy-water-graph-card.ts +++ b/src/panels/lovelace/cards/energy/hui-energy-water-graph-card.ts @@ -17,13 +17,7 @@ import { import { customElement, property, state } from "lit/decorators"; import { classMap } from "lit/directives/class-map"; import memoizeOne from "memoize-one"; -import { - hex2rgb, - lab2rgb, - rgb2hex, - rgb2lab, -} from "../../../../common/color/convert-color"; -import { labBrighten, labDarken } from "../../../../common/color/lab"; +import { getEnergyColor } from "./common/color"; import { formatNumber } from "../../../../common/number/format_number"; import "../../../../components/chart/ha-chart-base"; import "../../../../components/ha-card"; @@ -202,16 +196,12 @@ export class HuiEnergyWaterGraphCard const datasets: ChartDataset<"bar", ScatterDataPoint[]>[] = []; const computedStyles = getComputedStyle(this); - const waterColor = computedStyles - .getPropertyValue("--energy-water-color") - .trim(); datasets.push( ...this._processDataSet( energyData.stats, energyData.statsMetadata, waterSources, - waterColor, computedStyles ) ); @@ -233,7 +223,6 @@ export class HuiEnergyWaterGraphCard energyData.statsCompare, energyData.statsMetadata, waterSources, - waterColor, computedStyles, true ) @@ -255,28 +244,12 @@ export class HuiEnergyWaterGraphCard statistics: Statistics, statisticsMetaData: Record, waterSources: WaterSourceTypeEnergyPreference[], - waterColor: string, computedStyles: CSSStyleDeclaration, compare = false ) { const data: ChartDataset<"bar", ScatterDataPoint[]>[] = []; waterSources.forEach((source, idx) => { - let borderColor = computedStyles - .getPropertyValue("--energy-water-color-" + idx) - .trim(); - if (borderColor.length === 0) { - const modifiedColor = - idx > 0 - ? this.hass.themes.darkMode - ? labBrighten(rgb2lab(hex2rgb(waterColor)), idx) - : labDarken(rgb2lab(hex2rgb(waterColor)), idx) - : undefined; - borderColor = modifiedColor - ? rgb2hex(lab2rgb(modifiedColor)) - : waterColor; - } - let prevStart: number | null = null; const waterConsumptionData: ScatterDataPoint[] = []; @@ -315,8 +288,22 @@ export class HuiEnergyWaterGraphCard source.stat_energy_from, statisticsMetaData[source.stat_energy_from] ), - borderColor: compare ? borderColor + "7F" : borderColor, - backgroundColor: compare ? borderColor + "32" : borderColor + "7F", + borderColor: getEnergyColor( + computedStyles, + this.hass.themes.darkMode, + false, + compare, + "--energy-water-color", + idx + ), + backgroundColor: getEnergyColor( + computedStyles, + this.hass.themes.darkMode, + true, + compare, + "--energy-water-color", + idx + ), data: waterConsumptionData, order: 1, stack: "water", diff --git a/yarn.lock b/yarn.lock index 1a0ff9d448..88c2e9af1d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4003,6 +4003,13 @@ __metadata: languageName: node linkType: hard +"@types/color-name@npm:^1": + version: 1.1.3 + resolution: "@types/color-name@npm:1.1.3" + checksum: 10/9060d16d0bce2cdf562d6da54e18c5f23e80308ccb58b725b9173a028818f27d8e01c8a5cd96952e76f11145a7388ed7d2f450fb4652f4760383834f2e698263 + languageName: node + linkType: hard + "@types/command-line-args@npm:^5.0.0": version: 5.2.3 resolution: "@types/command-line-args@npm:5.2.3" @@ -9605,6 +9612,7 @@ __metadata: "@types/babel__plugin-transform-runtime": "npm:7.9.5" "@types/chromecast-caf-receiver": "npm:6.0.13" "@types/chromecast-caf-sender": "npm:1.0.8" + "@types/color-name": "npm:^1" "@types/glob": "npm:8.1.0" "@types/html-minifier-terser": "npm:7.0.2" "@types/js-yaml": "npm:4.0.9" @@ -9635,6 +9643,7 @@ __metadata: babel-plugin-template-html-minifier: "npm:4.1.0" chai: "npm:5.1.0" chart.js: "npm:4.4.1" + color-name: "npm:~1.1.4" comlink: "npm:4.4.1" core-js: "npm:3.36.0" cropperjs: "npm:1.6.1"