mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-24 09:46:36 +00:00
Use entity based color for state history (#14330)
This commit is contained in:
parent
238e844068
commit
1c03dc9b77
@ -5,14 +5,14 @@ import { computeDomain } from "./compute_domain";
|
||||
const NORMAL_UNKNOWN_DOMAIN = ["button", "input_button", "scene"];
|
||||
const NORMAL_OFF_DOMAIN = ["script"];
|
||||
|
||||
export function stateActive(stateObj: HassEntity): boolean {
|
||||
export function stateActive(stateObj: HassEntity, state?: string): boolean {
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
const state = stateObj.state;
|
||||
const compareState = state !== undefined ? state : stateObj?.state;
|
||||
|
||||
if (
|
||||
OFF_STATES.includes(state) &&
|
||||
!(NORMAL_UNKNOWN_DOMAIN.includes(domain) && state === "unknown") &&
|
||||
!(NORMAL_OFF_DOMAIN.includes(domain) && state === "script")
|
||||
OFF_STATES.includes(compareState) &&
|
||||
!(NORMAL_UNKNOWN_DOMAIN.includes(domain) && compareState === "unknown") &&
|
||||
!(NORMAL_OFF_DOMAIN.includes(domain) && compareState === "script")
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
@ -20,16 +20,16 @@ export function stateActive(stateObj: HassEntity): boolean {
|
||||
// Custom cases
|
||||
switch (domain) {
|
||||
case "cover":
|
||||
return state === "open" || state === "opening";
|
||||
return compareState === "open" || compareState === "opening";
|
||||
case "device_tracker":
|
||||
case "person":
|
||||
return state !== "not_home";
|
||||
return compareState !== "not_home";
|
||||
case "media-player":
|
||||
return state !== "idle" && state !== "standby";
|
||||
return compareState !== "idle" && compareState !== "standby";
|
||||
case "vacuum":
|
||||
return state === "on" || state === "cleaning";
|
||||
return compareState === "on" || compareState === "cleaning";
|
||||
case "plant":
|
||||
return state === "problem";
|
||||
return compareState === "problem";
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
|
@ -10,12 +10,12 @@ import { sensorColor } from "./color/sensor_color";
|
||||
import { computeDomain } from "./compute_domain";
|
||||
import { stateActive } from "./state_active";
|
||||
|
||||
export const stateColorCss = (stateObj?: HassEntity) => {
|
||||
if (!stateObj || !stateActive(stateObj)) {
|
||||
export const stateColorCss = (stateObj?: HassEntity, state?: string) => {
|
||||
if (!stateObj || !stateActive(stateObj, state)) {
|
||||
return `var(--rgb-disabled-color)`;
|
||||
}
|
||||
|
||||
const color = stateColor(stateObj);
|
||||
const color = stateColor(stateObj, state);
|
||||
|
||||
if (color) {
|
||||
return `var(--rgb-state-${color}-color)`;
|
||||
@ -24,13 +24,13 @@ export const stateColorCss = (stateObj?: HassEntity) => {
|
||||
return `var(--rgb-primary-color)`;
|
||||
};
|
||||
|
||||
export const stateColor = (stateObj: HassEntity) => {
|
||||
const state = stateObj.state;
|
||||
export const stateColor = (stateObj: HassEntity, state?: string) => {
|
||||
const compareState = state !== undefined ? state : stateObj?.state;
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
|
||||
switch (domain) {
|
||||
case "alarm_control_panel":
|
||||
return alarmControlPanelColor(state);
|
||||
return alarmControlPanelColor(compareState);
|
||||
|
||||
case "binary_sensor":
|
||||
return binarySensorColor(stateObj);
|
||||
@ -39,10 +39,10 @@ export const stateColor = (stateObj: HassEntity) => {
|
||||
return coverColor(stateObj);
|
||||
|
||||
case "climate":
|
||||
return climateColor(state);
|
||||
return climateColor(compareState);
|
||||
|
||||
case "lock":
|
||||
return lockColor(state);
|
||||
return lockColor(compareState);
|
||||
|
||||
case "light":
|
||||
return "light";
|
||||
@ -64,7 +64,7 @@ export const stateColor = (stateObj: HassEntity) => {
|
||||
return "vacuum";
|
||||
|
||||
case "sun":
|
||||
return state === "above_horizon" ? "sun-day" : "sun-night";
|
||||
return compareState === "above_horizon" ? "sun-day" : "sun-night";
|
||||
|
||||
case "update":
|
||||
return updateIsInstalling(stateObj as UpdateEntity)
|
||||
|
@ -3,8 +3,10 @@ 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 { computeDomain } from "../../common/entity/compute_domain";
|
||||
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";
|
||||
@ -12,65 +14,55 @@ import { HomeAssistant } from "../../types";
|
||||
import { MIN_TIME_BETWEEN_UPDATES } from "./ha-chart-base";
|
||||
import type { TimeLineData } from "./timeline-chart/const";
|
||||
|
||||
/** Binary sensor device classes for which the static colors for on/off are NOT inverted.
|
||||
* List the ones were "on" = good or normal state => should be rendered "green".
|
||||
* Note: It is now a "not inverted" list (compared to the past) since we now have more inverted ones.
|
||||
*/
|
||||
const BINARY_SENSOR_DEVICE_CLASS_COLOR_NOT_INVERTED = new Set([
|
||||
"battery_charging",
|
||||
"connectivity",
|
||||
"light",
|
||||
"moving",
|
||||
"plug",
|
||||
"power",
|
||||
"presence",
|
||||
"running",
|
||||
]);
|
||||
|
||||
const STATIC_STATE_COLORS = new Set([
|
||||
"on",
|
||||
"off",
|
||||
"home",
|
||||
"not_home",
|
||||
"unavailable",
|
||||
"unknown",
|
||||
"idle",
|
||||
]);
|
||||
|
||||
const stateColorTokenMap: Map<string, string> = new Map();
|
||||
const stateColorMap: Map<string, string> = new Map();
|
||||
|
||||
let colorIndex = 0;
|
||||
|
||||
const invertOnOff = (entityState?: HassEntity) =>
|
||||
entityState &&
|
||||
computeDomain(entityState.entity_id) === "binary_sensor" &&
|
||||
"device_class" in entityState.attributes &&
|
||||
!BINARY_SENSOR_DEVICE_CLASS_COLOR_NOT_INVERTED.has(
|
||||
entityState.attributes.device_class!
|
||||
);
|
||||
export const getStateColorToken = (
|
||||
stateString: string,
|
||||
entityState: HassEntity
|
||||
) => {
|
||||
if (!stateActive(entityState, stateString)) {
|
||||
return `disabled`;
|
||||
}
|
||||
const color = stateColor(entityState, stateString);
|
||||
if (color) {
|
||||
return `state-${color}`;
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
const getColor = (
|
||||
stateString: string,
|
||||
entityState: HassEntity,
|
||||
computedStyles: CSSStyleDeclaration
|
||||
) => {
|
||||
// Inversion is only valid for "on" or "off" state
|
||||
if (
|
||||
(stateString === "on" || stateString === "off") &&
|
||||
invertOnOff(entityState)
|
||||
) {
|
||||
stateString = stateString === "on" ? "off" : "on";
|
||||
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);
|
||||
}
|
||||
if (STATIC_STATE_COLORS.has(stateString)) {
|
||||
const color = computedStyles.getPropertyValue(
|
||||
`--state-${stateString}-color`
|
||||
);
|
||||
stateColorMap.set(stateString, color);
|
||||
return color;
|
||||
}
|
||||
const color = getGraphColorByIndex(colorIndex, computedStyles);
|
||||
colorIndex++;
|
||||
stateColorMap.set(stateString, color);
|
||||
|
@ -45,6 +45,7 @@ export class MoreInfoLogbook extends LitElement {
|
||||
narrow
|
||||
no-icon
|
||||
no-name
|
||||
show-indicator
|
||||
relative-time
|
||||
></ha-logbook>
|
||||
`;
|
||||
|
@ -11,12 +11,15 @@ import {
|
||||
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 { 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 "../../components/entity/state-badge";
|
||||
import "../../components/ha-circular-progress";
|
||||
import "../../components/ha-relative-time";
|
||||
@ -67,6 +70,9 @@ class HaLogbookRenderer extends LitElement {
|
||||
@property({ type: Boolean, attribute: "virtualize", reflect: true })
|
||||
public virtualize = false;
|
||||
|
||||
@property({ type: Boolean, attribute: "show-indicator" })
|
||||
public showIndicator = false;
|
||||
|
||||
@property({ type: Boolean, attribute: "no-icon" })
|
||||
public noIcon = false;
|
||||
|
||||
@ -132,7 +138,7 @@ class HaLogbookRenderer extends LitElement {
|
||||
if (!item || index === undefined) {
|
||||
return html``;
|
||||
}
|
||||
const previous = this.entries[index - 1];
|
||||
const previous = this.entries[index - 1] as LogbookEntry | undefined;
|
||||
const seenEntityIds: string[] = [];
|
||||
const currentStateObj = item.entity_id
|
||||
? this.hass.states[item.entity_id]
|
||||
@ -199,6 +205,7 @@ class HaLogbookRenderer extends LitElement {
|
||||
></state-badge>
|
||||
`
|
||||
: ""}
|
||||
${this.showIndicator ? this._renderIndicator(item) : ""}
|
||||
<div class="message-relative_time">
|
||||
<div class="message">
|
||||
${!this.noName // Used for more-info panel (single entity case)
|
||||
@ -253,6 +260,23 @@ class HaLogbookRenderer extends LitElement {
|
||||
});
|
||||
}
|
||||
|
||||
private _renderIndicator(item: LogbookEntry) {
|
||||
const stateObj = this.hass.states[item.entity_id!] as
|
||||
| HassEntity
|
||||
| undefined;
|
||||
|
||||
const color =
|
||||
stateObj && stateActive(stateObj, item.state)
|
||||
? stateColorCss(stateObj, item.state)
|
||||
: undefined;
|
||||
|
||||
const style = {
|
||||
"--indicator-color": color,
|
||||
};
|
||||
|
||||
return html` <div class="indicator" style=${styleMap(style)}></div> `;
|
||||
}
|
||||
|
||||
private _renderMessage(
|
||||
item: LogbookEntry,
|
||||
seenEntityIds: string[],
|
||||
@ -541,6 +565,7 @@ class HaLogbookRenderer extends LitElement {
|
||||
}
|
||||
|
||||
.entry {
|
||||
position: relative;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
line-height: 2em;
|
||||
@ -551,6 +576,20 @@ class HaLogbookRenderer extends LitElement {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.indicator {
|
||||
background-color: rgb(
|
||||
var(--indicator-color, var(--rgb-disabled-color))
|
||||
);
|
||||
height: 8px;
|
||||
width: 8px;
|
||||
border-radius: 4px;
|
||||
flex-shrink: 0;
|
||||
margin-right: 12px;
|
||||
margin-inline-start: initial;
|
||||
margin-inline-end: 12px;
|
||||
direction: var(--direction);
|
||||
}
|
||||
|
||||
ha-icon-next {
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
|
@ -65,6 +65,9 @@ export class HaLogbook extends LitElement {
|
||||
@property({ type: Boolean, attribute: "no-name" })
|
||||
public noName = false;
|
||||
|
||||
@property({ type: Boolean, attribute: "show-indicator" })
|
||||
public showIndicator = false;
|
||||
|
||||
@property({ type: Boolean, attribute: "relative-time" })
|
||||
public relativeTime = false;
|
||||
|
||||
@ -126,6 +129,7 @@ export class HaLogbook extends LitElement {
|
||||
.virtualize=${this.virtualize}
|
||||
.noIcon=${this.noIcon}
|
||||
.noName=${this.noName}
|
||||
.showIndicator=${this.showIndicator}
|
||||
.relativeTime=${this.relativeTime}
|
||||
.entries=${this._logbookEntries}
|
||||
.traceContexts=${this._traceContexts}
|
||||
|
Loading…
x
Reference in New Issue
Block a user