diff --git a/src/components/chart/state-history-chart-timeline.ts b/src/components/chart/state-history-chart-timeline.ts index 680bf9ae2c..3425ccbd6a 100644 --- a/src/components/chart/state-history-chart-timeline.ts +++ b/src/components/chart/state-history-chart-timeline.ts @@ -1,73 +1,14 @@ import type { ChartData, ChartDataset, ChartOptions } from "chart.js"; -import { HassEntity } from "home-assistant-js-websocket"; import { css, CSSResultGroup, html, LitElement, PropertyValues } from "lit"; import { customElement, property, state } from "lit/decorators"; -import { getGraphColorByIndex } from "../../common/color/colors"; -import { rgb2hex } from "../../common/color/convert-color"; import { formatDateTimeWithSeconds } from "../../common/datetime/format_date_time"; -import { stateActive } from "../../common/entity/state_active"; -import { stateColor } from "../../common/entity/state_color"; import { numberFormatToLocale } from "../../common/number/format_number"; import { computeRTL } from "../../common/util/compute_rtl"; import { TimelineEntity } from "../../data/history"; import { HomeAssistant } from "../../types"; import { MIN_TIME_BETWEEN_UPDATES } from "./ha-chart-base"; import type { TimeLineData } from "./timeline-chart/const"; - -const stateColorTokenMap: Map = new Map(); -const stateColorMap: Map = new Map(); - -let colorIndex = 0; - -export const getStateColorToken = ( - stateString: string, - entityState?: HassEntity -) => { - if (!entityState || !stateActive(entityState, stateString)) { - return `disabled`; - } - const color = stateColor(entityState, stateString); - if (color) { - return `state-${color}`; - } - return undefined; -}; - -const getColor = ( - stateString: string, - computedStyles: CSSStyleDeclaration, - entityState?: HassEntity -) => { - const stateColorToken = getStateColorToken(stateString, entityState); - - if (stateColorToken) { - if (stateColorTokenMap.has(stateColorToken)) { - return stateColorTokenMap.get(stateColorToken); - } - const value = computedStyles.getPropertyValue( - `--rgb-${stateColorToken}-color` - ); - - if (value) { - const parsedValue = value.split(",").map((v) => Number(v)) as [ - number, - number, - number - ]; - const hexValue = rgb2hex(parsedValue); - stateColorTokenMap.set(stateColorToken, hexValue); - return hexValue; - } - } - - if (stateColorMap.has(stateString)) { - return stateColorMap.get(stateString); - } - const color = getGraphColorByIndex(colorIndex, computedStyles); - colorIndex++; - stateColorMap.set(stateString, color); - return color; -}; +import { computeTimelineColor } from "./timeline-chart/timeline-color"; @customElement("state-history-chart-timeline") export class StateHistoryChartTimeline extends LitElement { @@ -270,7 +211,7 @@ export class StateHistoryChartTimeline extends LitElement { start: prevLastChanged, end: newLastChanged, label: locState, - color: getColor( + color: computeTimelineColor( prevState, computedStyles, this.hass.states[stateInfo.entity_id] @@ -288,7 +229,7 @@ export class StateHistoryChartTimeline extends LitElement { start: prevLastChanged, end: endTime, label: locState, - color: getColor( + color: computeTimelineColor( prevState, computedStyles, this.hass.states[stateInfo.entity_id] diff --git a/src/components/chart/timeline-chart/timeline-color.ts b/src/components/chart/timeline-chart/timeline-color.ts new file mode 100644 index 0000000000..012c9315f6 --- /dev/null +++ b/src/components/chart/timeline-chart/timeline-color.ts @@ -0,0 +1,99 @@ +import { HassEntity } from "home-assistant-js-websocket"; +import { getGraphColorByIndex } from "../../../common/color/colors"; +import { lab2hex, rgb2hex, rgb2lab } from "../../../common/color/convert-color"; +import { labBrighten } from "../../../common/color/lab"; +import { computeDomain } from "../../../common/entity/compute_domain"; +import { stateActive } from "../../../common/entity/state_active"; +import { stateColor } from "../../../common/entity/state_color"; + +const DOMAIN_STATE_SHADES: Record> = { + media_player: { + paused: 0.5, + idle: 1, + }, + vacuum: { + returning: 0.5, + }, +}; + +const cssColorMap: Map = new Map(); + +function cssToRgb( + cssVariable: string, + computedStyles: CSSStyleDeclaration +): [number, number, number] | undefined { + if (!cssVariable.startsWith("--rgb")) { + return undefined; + } + + if (cssColorMap.has(cssVariable)) { + return cssColorMap.get(cssVariable)!; + } + + const value = computedStyles.getPropertyValue(cssVariable); + + if (!value) return undefined; + + const rgb = value.split(",").map((v) => Number(v)) as [ + number, + number, + number + ]; + cssColorMap.set(cssVariable, rgb); + + return rgb; +} + +function computeTimelineStateColor( + state: string, + computedStyles: CSSStyleDeclaration, + stateObj?: HassEntity +): string | undefined { + if (!stateObj || !stateActive(stateObj, state)) { + const rgb = cssToRgb("--rgb-disabled-color", computedStyles); + if (!rgb) return undefined; + return rgb2hex(rgb); + } + const color = stateColor(stateObj, state); + + if (!color) return undefined; + + const domain = computeDomain(stateObj.entity_id); + + const rgb = cssToRgb(`--rgb-state-${color}-color`, computedStyles); + + if (!rgb) return undefined; + + const shade = DOMAIN_STATE_SHADES[domain]?.[state] as number | number; + if (!shade) { + return rgb2hex(rgb); + } + return lab2hex(labBrighten(rgb2lab(rgb), shade)); +} + +let colorIndex = 0; +const stateColorMap: Map = new Map(); + +function computeTimeLineGenericColor( + state: string, + computedStyles: CSSStyleDeclaration +): string { + if (stateColorMap.has(state)) { + return stateColorMap.get(state)!; + } + const color = getGraphColorByIndex(colorIndex, computedStyles); + colorIndex++; + stateColorMap.set(state, color); + return color; +} + +export function computeTimelineColor( + state: string, + computedStyles: CSSStyleDeclaration, + stateObj?: HassEntity +): string { + return ( + computeTimelineStateColor(state, computedStyles, stateObj) || + computeTimeLineGenericColor(state, computedStyles) + ); +} diff --git a/src/panels/logbook/ha-logbook-renderer.ts b/src/panels/logbook/ha-logbook-renderer.ts index adf30e47f5..47edea75ce 100644 --- a/src/panels/logbook/ha-logbook-renderer.ts +++ b/src/panels/logbook/ha-logbook-renderer.ts @@ -1,5 +1,6 @@ import "@lit-labs/virtualizer"; import { VisibilityChangedEvent } from "@lit-labs/virtualizer/Virtualizer"; +import type { HassEntity } from "home-assistant-js-websocket"; import { css, CSSResultGroup, @@ -8,37 +9,35 @@ import { PropertyValues, TemplateResult, } from "lit"; -import type { HassEntity } from "home-assistant-js-websocket"; import { customElement, eventOptions, property } from "lit/decorators"; import { classMap } from "lit/directives/class-map"; import { styleMap } from "lit/directives/style-map"; +import { isComponentLoaded } from "../../common/config/is_component_loaded"; import { formatDate } from "../../common/datetime/format_date"; import { formatTimeWithSeconds } from "../../common/datetime/format_time"; import { restoreScroll } from "../../common/decorators/restore-scroll"; import { fireEvent } from "../../common/dom/fire_event"; import { computeDomain } from "../../common/entity/compute_domain"; -import { isComponentLoaded } from "../../common/config/is_component_loaded"; -import { stateActive } from "../../common/entity/state_active"; -import { stateColorCss } from "../../common/entity/state_color"; +import { navigate } from "../../common/navigate"; +import { computeTimelineColor } from "../../components/chart/timeline-chart/timeline-color"; import "../../components/entity/state-badge"; import "../../components/ha-circular-progress"; +import "../../components/ha-icon-next"; import "../../components/ha-relative-time"; import { createHistoricState, - localizeTriggerSource, localizeStateMessage, + localizeTriggerSource, LogbookEntry, } from "../../data/logbook"; import { TraceContexts } from "../../data/trace"; import { + buttonLinkStyle, haStyle, haStyleScrollbar, - buttonLinkStyle, } from "../../resources/styles"; import { HomeAssistant } from "../../types"; import { brandsUrl } from "../../util/brands-url"; -import "../../components/ha-icon-next"; -import { navigate } from "../../common/navigate"; declare global { interface HASSDomEvents { @@ -264,14 +263,15 @@ class HaLogbookRenderer extends LitElement { const stateObj = this.hass.states[item.entity_id!] as | HassEntity | undefined; + const computedStyles = getComputedStyle(this); const color = - stateObj && stateActive(stateObj, item.state) - ? stateColorCss(stateObj, item.state) + item.state !== undefined + ? computeTimelineColor(item.state, computedStyles, stateObj) : undefined; const style = { - "--indicator-color": color, + backgroundColor: color, }; return html`
`; @@ -577,9 +577,7 @@ class HaLogbookRenderer extends LitElement { } .indicator { - background-color: rgb( - var(--indicator-color, var(--rgb-disabled-color)) - ); + background-color: rgb(var(--rgb-disabled-color)); height: 8px; width: 8px; border-radius: 4px;