UNPKG

lovelace-timer-bar-card

Version:

Progress bar display for Home Assistant timers

460 lines (421 loc) 14 kB
/** Styles and functions from the Mushroom Cards Project by Paul Bottein https://github.com/piitaya/lovelace-mushroom/ Licensed under Apache 2.0 */ import { computeDomain, HomeAssistant } from "custom-card-helpers"; import { css, html, TemplateResult } from "lit"; import { HassEntity } from "src/types"; export const defaultColorCss = css` --default-red: 244, 67, 54; --default-pink: 233, 30, 99; --default-purple: 156, 39, 176; --default-deep-purple: 103, 58, 183; --default-indigo: 63, 81, 181; --default-blue: 33, 150, 243; --default-light-blue: 3, 169, 244; --default-cyan: 0, 188, 212; --default-teal: 0, 150, 136; --default-green: 76, 175, 80; --default-light-green: 139, 195, 74; --default-lime: 205, 220, 57; --default-yellow: 255, 235, 59; --default-amber: 255, 193, 7; --default-orange: 255, 152, 0; --default-deep-orange: 255, 87, 34; --default-brown: 121, 85, 72; --default-grey: 158, 158, 158; --default-blue-grey: 96, 125, 139; --default-black: 0, 0, 0; --default-white: 255, 255, 255; --default-disabled: 189, 189, 189; `; export const defaultDarkColorCss = css` --default-disabled: 111, 111, 111; `; export const themeVariables = css` --spacing: var(--mush-spacing, 10px); /* Title */ --title-padding: var(--mush-title-padding, 24px 12px 8px); --title-spacing: var(--mush-title-spacing, 8px); --title-font-size: var(--mush-title-font-size, 24px); --title-font-weight: var(--mush-title-font-weight, normal); --title-line-height: var(--mush-title-line-height, 32px); --title-color: var(--mush-title-color, var(--primary-text-color)); --title-letter-spacing: var(--mush-title-letter-spacing, -0.288px); --subtitle-font-size: var(--mush-subtitle-font-size, 16px); --subtitle-font-weight: var(--mush-subtitle-font-weight, normal); --subtitle-line-height: var(--mush-subtitle-line-height, 24px); --subtitle-color: var(--mush-subtitle-color, var(--secondary-text-color)); --subtitle-letter-spacing: var(--mush-subtitle-letter-spacing, 0px); /* Card */ --card-primary-font-size: var(--mush-card-primary-font-size, 14px); --card-secondary-font-size: var(--mush-card-secondary-font-size, 12px); --card-primary-font-weight: var(--mush-card-primary-font-weight, 500); --card-secondary-font-weight: var(--mush-card-secondary-font-weight, 400); --card-primary-line-height: var(--mush-card-primary-line-height, 20px); --card-secondary-line-height: var(--mush-card-secondary-line-height, 16px); --card-primary-color: var( --mush-card-primary-color, var(--primary-text-color) ); --card-secondary-color: var( --mush-card-secondary-color, var(--primary-text-color) ); --card-primary-letter-spacing: var(--mush-card-primary-letter-spacing, 0.1px); --card-secondary-letter-spacing: var( --mush-card-secondary-letter-spacing, 0.4px ); /* Chips */ --chip-spacing: var(--mush-chip-spacing, 8px); --chip-padding: var(--mush-chip-padding, 0 0.25em); --chip-height: var(--mush-chip-height, 36px); --chip-border-radius: var(--mush-chip-border-radius, 19px); --chip-border-width: var( --mush-chip-border-width, var(--ha-card-border-width, 1px) ); --chip-border-color: var( --mush-chip-border-color, var(--ha-card-border-color, var(--divider-color)) ); --chip-box-shadow: var( --mush-chip-box-shadow, var(--ha-card-box-shadow, "none") ); --chip-font-size: var(--mush-chip-font-size, 0.3em); --chip-font-weight: var(--mush-chip-font-weight, bold); --chip-icon-size: var(--mush-chip-icon-size, 0.5em); --chip-avatar-padding: var(--mush-chip-avatar-padding, 0.1em); --chip-avatar-border-radius: var(--mush-chip-avatar-border-radius, 50%); --chip-background: var( --mush-chip-background, var(--ha-card-background, var(--card-background-color, white)) ); /* Controls */ --control-border-radius: var(--mush-control-border-radius, 12px); --control-height: var(--mush-control-height, 42px); --control-button-ratio: var(--mush-control-button-ratio, 1); --control-icon-size: var(--mush-control-icon-size, 0.5em); --control-spacing: var(--mush-control-spacing, 12px); /* Slider */ --slider-threshold: var(--mush-slider-threshold); /* Input Number */ --input-number-debounce: var(--mush-input-number-debounce); /* Layout */ --layout-align: var(--mush-layout-align, center); /* Badge */ --badge-size: var(--mush-badge-size, 16px); --badge-icon-size: var(--mush-badge-icon-size, 0.75em); --badge-border-radius: var(--mush-badge-border-radius, 50%); /* Icon */ --icon-border-radius: var(--mush-icon-border-radius, 50%); --icon-size: var(--mush-icon-size, 36px); --icon-symbol-size: var(--mush-icon-symbol-size, 0.6em); `; export const themeColorCss = css` /* RGB */ /* Standard colors */ --rgb-red: var(--mush-rgb-red, var(--default-red)); --rgb-pink: var(--mush-rgb-pink, var(--default-pink)); --rgb-purple: var(--mush-rgb-purple, var(--default-purple)); --rgb-deep-purple: var(--mush-rgb-deep-purple, var(--default-deep-purple)); --rgb-indigo: var(--mush-rgb-indigo, var(--default-indigo)); --rgb-blue: var(--mush-rgb-blue, var(--default-blue)); --rgb-light-blue: var(--mush-rgb-light-blue, var(--default-light-blue)); --rgb-cyan: var(--mush-rgb-cyan, var(--default-cyan)); --rgb-teal: var(--mush-rgb-teal, var(--default-teal)); --rgb-green: var(--mush-rgb-green, var(--default-green)); --rgb-light-green: var(--mush-rgb-light-green, var(--default-light-green)); --rgb-lime: var(--mush-rgb-lime, var(--default-lime)); --rgb-yellow: var(--mush-rgb-yellow, var(--default-yellow)); --rgb-amber: var(--mush-rgb-amber, var(--default-amber)); --rgb-orange: var(--mush-rgb-orange, var(--default-orange)); --rgb-deep-orange: var(--mush-rgb-deep-orange, var(--default-deep-orange)); --rgb-brown: var(--mush-rgb-brown, var(--default-brown)); --rgb-light-grey: var(--mush-rgb-light-grey, var(--default-light-grey)); --rgb-grey: var(--mush-rgb-grey, var(--default-grey)); --rgb-dark-grey: var(--mush-rgb-dark-grey, var(--default-dark-grey)); --rgb-blue-grey: var(--mush-rgb-blue-grey, var(--default-blue-grey)); --rgb-black: var(--mush-rgb-black, var(--default-black)); --rgb-white: var(--mush-rgb-white, var(--default-white)); --rgb-disabled: var(--mush-rgb-disabled, var(--default-disabled)); /* Action colors */ --rgb-info: var(--mush-rgb-info, var(--rgb-blue)); --rgb-success: var(--mush-rgb-success, var(--rgb-green)); --rgb-warning: var(--mush-rgb-warning, var(--rgb-orange)); --rgb-danger: var(--mush-rgb-danger, var(--rgb-red)); /* State colors */ --rgb-state-vacuum: var(--mush-rgb-state-vacuum, var(--rgb-teal)); --rgb-state-fan: var(--mush-rgb-state-fan, var(--rgb-green)); --rgb-state-light: var(--mush-rgb-state-light, var(--rgb-orange)); --rgb-state-entity: var(--mush-rgb-state-entity, var(--rgb-blue)); --rgb-state-media-player: var( --mush-rgb-state-media-player, var(--rgb-indigo) ); --rgb-state-lock: var(--mush-rgb-state-lock, var(--rgb-blue)); --rgb-state-number: var(--mush-rgb-state-number, var(--rgb-blue)); --rgb-state-humidifier: var(--mush-rgb-state-humidifier, var(--rgb-purple)); /* State alarm colors */ --rgb-state-alarm-disarmed: var( --mush-rgb-state-alarm-disarmed, var(--rgb-info) ); --rgb-state-alarm-armed: var( --mush-rgb-state-alarm-armed, var(--rgb-success) ); --rgb-state-alarm-triggered: var( --mush-rgb-state-alarm-triggered, var(--rgb-danger) ); /* State person colors */ --rgb-state-person-home: var( --mush-rgb-state-person-home, var(--rgb-success) ); --rgb-state-person-not-home: var( --mush-rgb-state-person-not-home, var(--rgb-danger) ); --rgb-state-person-zone: var(--mush-rgb-state-person-zone, var(--rgb-info)); --rgb-state-person-unknown: var( --mush-rgb-state-person-unknown, var(--rgb-grey) ); /* State update colors */ --rgb-state-update-on: var(--mush-rgb-state-update-on, var(--rgb-orange)); --rgb-state-update-off: var(--mush-rgb-update-off, var(--rgb-green)); --rgb-state-update-installing: var( --mush-rgb-update-installing, var(--rgb-blue) ); /* State lock colors */ --rgb-state-lock-locked: var(--mush-rgb-state-lock-locked, var(--rgb-green)); --rgb-state-lock-unlocked: var( --mush-rgb-state-lock-unlocked, var(--rgb-red) ); --rgb-state-lock-pending: var( --mush-rgb-state-lock-pending, var(--rgb-orange) ); /* State cover colors */ --rgb-state-cover-open: var(--mush-rgb-state-cover-open, var(--rgb-blue)); --rgb-state-cover-closed: var( --mush-rgb-state-cover-closed, var(--rgb-disabled) ); /* State climate colors */ --rgb-state-climate-auto: var( --mush-rgb-state-climate-auto, var(--rgb-green) ); --rgb-state-climate-cool: var(--mush-rgb-state-climate-cool, var(--rgb-blue)); --rgb-state-climate-dry: var(--mush-rgb-state-climate-dry, var(--rgb-orange)); --rgb-state-climate-fan-only: var( --mush-rgb-state-climate-fan-only, var(--rgb-teal) ); --rgb-state-climate-heat: var( --mush-rgb-state-climate-heat, var(--rgb-deep-orange) ); --rgb-state-climate-heat-cool: var( --mush-rgb-state-climate-heat-cool, var(--rgb-green) ); --rgb-state-climate-idle: var( --mush-rgb-state-climate-idle, var(--rgb-disabled) ); --rgb-state-climate-off: var( --mush-rgb-state-climate-off, var(--rgb-disabled) ); `; export const cardStyle = css` ha-card { box-sizing: border-box; display: flex; flex-direction: column; justify-content: var(--layout-align); height: auto; display: flex; flex-direction: column; } ha-card.fill-container { height: 100%; } :host([layout="grid"]) ha-card { height: 100%; } .actions { display: flex; flex-direction: row; align-items: center; justify-content: flex-start; overflow-x: auto; overflow-y: hidden; scrollbar-width: none; /* Firefox */ -ms-overflow-style: none; /* IE 10+ */ padding: var(--control-spacing); padding-top: 0; box-sizing: border-box; gap: var(--control-spacing); } .actions::-webkit-scrollbar { background: transparent; /* Chrome/Safari/Webkit */ height: 0px; } .unavailable { --main-color: rgb(var(--rgb-warning)); } .not-found { --main-color: rgb(var(--rgb-danger)); } mushroom-state-item[disabled] { cursor: initial; } `; export const UNAVAILABLE = "unavailable"; export const UNKNOWN = "unknown"; export const ON = "on"; export const OFF = "off"; const OFF_STATES = [UNAVAILABLE, UNKNOWN, OFF]; export function isActive(stateObj: HassEntity) { /** https://github.com/piitaya/lovelace-mushroom/blob/f33c910b8863fa2229ee0a73e6fbf515f3bc2788/src/ha/data/entity.ts */ const domain = computeDomain(stateObj.entity_id); const state = stateObj.state; if (["button", "input_button", "scene"].includes(domain)) { return state !== UNAVAILABLE; } if (OFF_STATES.includes(state)) { return false; } // Custom cases switch (domain) { case "cover": return !["closed", "closing"].includes(state); case "device_tracker": case "person": return state !== "not_home"; case "media_player": return state !== "standby"; case "vacuum": return !["idle", "docked", "paused"].includes(state); case "plant": return state === "problem"; default: return true; } } export const COLORS = [ "primary", "accent", "red", "pink", "purple", "deep-purple", "indigo", "blue", "light-blue", "cyan", "teal", "green", "light-green", "lime", "yellow", "amber", "orange", "deep-orange", "brown", "grey", "blue-grey", "black", "white", "disabled", ]; export function computeRgbColor(color: string): string { if (color === "primary" || color === "accent") { return `var(--rgb-${color}-color)`; } if (COLORS.includes(color)) { return `var(--rgb-${color})`; } else if (color.startsWith("#")) { const b = parseInt(color.substring(1), 16); if (isNaN(b)) return ""; return [(b >> 16) & 255, (b >> 8) & 255, b & 255].join(", "); } return color; } const TIMESTAMP_STATE_DOMAINS = ["button", "input_button", "scene"]; export const INFOS = [ "name", "state", "last-changed", "last-updated", "none", ] as const; export type Info = (typeof INFOS)[number]; export const ICON_TYPES = ["icon", "entity-picture", "none"] as const; export type IconType = (typeof ICON_TYPES)[number]; export function isAvailable(stateObj: HassEntity) { return stateObj.state !== UNAVAILABLE; } export function isOff(stateObj: HassEntity) { return stateObj.state === OFF; } export function isUnknown(stateObj: HassEntity) { return stateObj.state === UNKNOWN; } export function computeInfoDisplay( info: Info, name: string, state: string | TemplateResult, stateObj: HassEntity, hass: HomeAssistant, ) { switch (info) { case "name": return name; case "state": const domain = stateObj.entity_id.split(".")[0]; if ( (stateObj.attributes.device_class === "timestamp" || TIMESTAMP_STATE_DOMAINS.includes(domain)) && isAvailable(stateObj) && !isUnknown(stateObj) ) { return html` <ha-relative-time .hass=${hass} .datetime=${stateObj.state} capitalize ></ha-relative-time> `; } else { return state; } case "last-changed": return html` <ha-relative-time .hass=${hass} .datetime=${stateObj.last_changed} capitalize ></ha-relative-time> `; case "last-updated": return html` <ha-relative-time .hass=${hass} .datetime=${stateObj.last_updated} capitalize ></ha-relative-time> `; case "none": return undefined; } }