Add state handling for non-light entities.

This commit is contained in:
Dermot Duffy
2022-02-12 07:30:39 -08:00
parent 3993dee11c
commit 6a32c66de0
3 changed files with 134 additions and 13 deletions
+28 -8
View File
@@ -1,5 +1,10 @@
import { HassEntity, MessageBase } from 'home-assistant-js-websocket'; import { HassEntity, MessageBase } from 'home-assistant-js-websocket';
import { HomeAssistant, handleActionConfig, hasAction, stateIcon } from 'custom-card-helpers'; import {
HomeAssistant,
handleActionConfig,
hasAction,
stateIcon,
} from 'custom-card-helpers';
import { StyleInfo } from 'lit/directives/style-map'; import { StyleInfo } from 'lit/directives/style-map';
import { ZodSchema, z } from 'zod'; import { ZodSchema, z } from 'zod';
import { isEqual } from 'lodash-es'; import { isEqual } from 'lodash-es';
@@ -359,6 +364,23 @@ function computeStyle(state: HassEntity): StyleInfo {
}; };
} }
/**
* Determine the string state of a given stateObj.
* From: https://github.com/home-assistant/frontend/blob/dev/src/common/entity/compute_active_state.ts
* @param stateObj The HassEntity object from `hass.states`.
* @returns A string state, e.g. 'on'.
*/
export const computeActiveState = (stateObj: HassEntity): string => {
const domain = stateObj.entity_id.split('.')[0];
let state = stateObj.state;
if (domain === 'climate') {
state = stateObj.attributes.hvac_action;
}
return state;
};
/** /**
* Use Home Assistant state to refresh state parameters for an item to be rendered. * Use Home Assistant state to refresh state parameters for an item to be rendered.
* @param hass Home Assistant object. * @param hass Home Assistant object.
@@ -373,11 +395,7 @@ export function refreshDynamicStateParameters(
return params; return params;
} }
const state = hass.states[params.entity]; const state = hass.states[params.entity];
if ( if (!!state && !!params.state_color) {
!!state &&
!!params.state_color &&
['on', 'active', 'home'].includes(state.state)
) {
params.style = { ...computeStyle(state), ...params.style }; params.style = { ...computeStyle(state), ...params.style };
} }
params.title = params.title ?? (state?.attributes?.friendly_name || params.entity); params.title = params.title ?? (state?.attributes?.friendly_name || params.entity);
@@ -545,9 +563,11 @@ export const frigateCardHandleActionConfig = (
* @param config The action config in question. * @param config The action config in question.
* @returns `true` if there's a real action defined, `false` otherwise. * @returns `true` if there's a real action defined, `false` otherwise.
*/ */
export const frigateCardHasAction = (config?: ActionType | ActionType[] | undefined): boolean => { export const frigateCardHasAction = (
config?: ActionType | ActionType[] | undefined,
): boolean => {
if (Array.isArray(config)) { if (Array.isArray(config)) {
return !!config.find((item) => hasAction(item)); return !!config.find((item) => hasAction(item));
} }
return hasAction(config); return hasAction(config);
} };
+25 -5
View File
@@ -1,7 +1,8 @@
import { HASSDomEvent, HomeAssistant } from 'custom-card-helpers'; import { computeStateDomain, HASSDomEvent, HomeAssistant } from 'custom-card-helpers';
import { CSSResultGroup, LitElement, TemplateResult, html, unsafeCSS } from 'lit'; import { CSSResultGroup, LitElement, TemplateResult, html, unsafeCSS } from 'lit';
import { customElement, property, state } from 'lit/decorators.js'; import { customElement, property, state } from 'lit/decorators.js';
import { classMap } from 'lit/directives/class-map.js'; import { classMap } from 'lit/directives/class-map.js';
import { ifDefined } from 'lit/directives/if-defined';
import { styleMap } from 'lit/directives/style-map.js'; import { styleMap } from 'lit/directives/style-map.js';
import { actionHandler } from '../action-handler-directive.js'; import { actionHandler } from '../action-handler-directive.js';
@@ -17,6 +18,7 @@ import type {
StateParameters, StateParameters,
} from '../types.js'; } from '../types.js';
import { import {
computeActiveState,
convertActionToFrigateCardCustomAction, convertActionToFrigateCardCustomAction,
frigateCardHandleActionConfig, frigateCardHandleActionConfig,
frigateCardHasAction, frigateCardHasAction,
@@ -218,10 +220,7 @@ export class FrigateCardMenu extends LitElement {
const svgPath = const svgPath =
stateParameters.icon === FRIGATE_BUTTON_MENU_ICON ? FRIGATE_ICON_FILLED : ''; stateParameters.icon === FRIGATE_BUTTON_MENU_ICON ? FRIGATE_ICON_FILLED : '';
if (button.type === 'custom:frigate-card-menu-state-icon') { if (this.hass && button.type === 'custom:frigate-card-menu-state-icon') {
if (!this.hass) {
return;
}
stateParameters = refreshDynamicStateParameters(this.hass, stateParameters); stateParameters = refreshDynamicStateParameters(this.hass, stateParameters);
} }
@@ -232,7 +231,28 @@ export class FrigateCardMenu extends LitElement {
button: true, button: true,
}; };
const stateObj = stateParameters.entity ? this.hass?.states[stateParameters.entity] : undefined;
const domain = stateObj ? computeStateDomain(stateObj) : undefined;
// =====================================================================================
// For `data-domain` and `data-state`, see: See
// https://github.com/home-assistant/frontend/blob/dev/src/components/entity/state-badge.ts#L54
// =====================================================================================
// Buttons are styled in a few ways (in order of precedence):
//
// - User provided style
// - Color/Brightness styling for the `light` domain (calculated in
// `refreshDynamicStateParameters`)
// - Static styling based on domain (`data-domain`) and state
// (`data-state`). This looks up a CSS style in `menu.scss`.
return html` <ha-icon-button return html` <ha-icon-button
data-domain=${ifDefined(
stateParameters.state_color || (domain === "light" && stateParameters.state_color !== false)
? domain
: undefined
)}
data-state=${stateObj ? computeActiveState(stateObj) : ""}
class="${classMap(classes)}" class="${classMap(classes)}"
style="${styleMap(stateParameters.style || {})}" style="${styleMap(stateParameters.style || {})}"
.actionHandler=${actionHandler({ .actionHandler=${actionHandler({
+81
View File
@@ -8,4 +8,85 @@ ha-icon-button.button {
/* Buttons can always be clicked */ /* Buttons can always be clicked */
pointer-events: auto; pointer-events: auto;
opacity: 0.9; opacity: 0.9;
}
// =====================================================================================
// From: https://github.com/home-assistant/frontend/blob/dev/src/common/style/icon_color_css.ts
// Modified for `ha-icon-button` instead of `ha-state-icon`.
// =====================================================================================
ha-icon-button[data-domain="alert"][data-state="on"],
ha-icon-button[data-domain="automation"][data-state="on"],
ha-icon-button[data-domain="binary_sensor"][data-state="on"],
ha-icon-button[data-domain="calendar"][data-state="on"],
ha-icon-button[data-domain="camera"][data-state="streaming"],
ha-icon-button[data-domain="cover"][data-state="open"],
ha-icon-button[data-domain="fan"][data-state="on"],
ha-icon-button[data-domain="humidifier"][data-state="on"],
ha-icon-button[data-domain="light"][data-state="on"],
ha-icon-button[data-domain="input_boolean"][data-state="on"],
ha-icon-button[data-domain="lock"][data-state="unlocked"],
ha-icon-button[data-domain="media_player"][data-state="on"],
ha-icon-button[data-domain="media_player"][data-state="paused"],
ha-icon-button[data-domain="media_player"][data-state="playing"],
ha-icon-button[data-domain="script"][data-state="on"],
ha-icon-button[data-domain="sun"][data-state="above_horizon"],
ha-icon-button[data-domain="switch"][data-state="on"],
ha-icon-button[data-domain="timer"][data-state="active"],
ha-icon-button[data-domain="vacuum"][data-state="cleaning"],
ha-icon-button[data-domain="group"][data-state="on"],
ha-icon-button[data-domain="group"][data-state="home"],
ha-icon-button[data-domain="group"][data-state="open"],
ha-icon-button[data-domain="group"][data-state="locked"],
ha-icon-button[data-domain="group"][data-state="problem"] {
color: var(--paper-item-icon-active-color, #fdd835);
}
ha-icon-button[data-domain="climate"][data-state="cooling"] {
color: var(--cool-color, var(--state-climate-cool-color));
}
ha-icon-button[data-domain="climate"][data-state="heating"] {
color: var(--heat-color, var(--state-climate-heat-color));
}
ha-icon-button[data-domain="climate"][data-state="drying"] {
color: var(--dry-color, var(--state-climate-dry-color));
}
ha-icon-button[data-domain="alarm_control_panel"] {
color: var(--alarm-color-armed, var(--label-badge-red));
}
ha-icon-button[data-domain="alarm_control_panel"][data-state="disarmed"] {
color: var(--alarm-color-disarmed, var(--label-badge-green));
}
ha-icon-button[data-domain="alarm_control_panel"][data-state="pending"],
ha-icon-button[data-domain="alarm_control_panel"][data-state="arming"] {
color: var(--alarm-color-pending, var(--label-badge-yellow));
animation: pulse 1s infinite;
}
ha-icon-button[data-domain="alarm_control_panel"][data-state="triggered"] {
color: var(--alarm-color-triggered, var(--label-badge-red));
animation: pulse 1s infinite;
}
@keyframes pulse {
0% {
opacity: 1;
}
50% {
opacity: 0;
}
100% {
opacity: 1;
}
}
ha-icon-button[data-domain="plant"][data-state="problem"],
ha-icon-button[data-domain="zwave"][data-state="dead"] {
color: var(--state-icon-error-color);
}
/* Color the icon if unavailable */
ha-icon-button[data-state="unavailable"] {
color: var(--state-unavailable-color);
} }