Add state handling for non-light entities.
This commit is contained in:
+28
-8
@@ -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
@@ -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({
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user