From fcfdad3d94d26b95b00904128192c1a0b0e6685c Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Tue, 29 Nov 2022 15:15:47 +0100 Subject: [PATCH] And vacuum commands extra for tile card (#14434) --- src/data/vacuum.ts | 74 ++++-- .../more-info/controls/more-info-vacuum.ts | 40 ++-- .../create-tile-extra-element.ts | 2 + .../config-elements/hui-tile-card-editor.ts | 10 +- .../hui-tile-card-extras-editor.ts | 3 +- .../hui-vacuum-commands-tile-extra-editor.ts | 101 ++++++++ .../lovelace/editor/hui-element-editor.ts | 8 +- .../lovelace/editor/hui-sub-element-editor.ts | 5 + .../hui-tile-extra-element-editor.ts | 10 +- .../hui-vacuum-commands-tile-extra.ts | 220 ++++++++++++++++++ src/panels/lovelace/tile-extra/tile-extras.ts | 8 +- src/panels/lovelace/tile-extra/types.ts | 22 +- src/panels/lovelace/types.ts | 8 +- src/translations/en.json | 11 + 14 files changed, 472 insertions(+), 50 deletions(-) create mode 100644 src/panels/lovelace/editor/config-elements/hui-vacuum-commands-tile-extra-editor.ts create mode 100644 src/panels/lovelace/tile-extra/hui-vacuum-commands-tile-extra.ts diff --git a/src/data/vacuum.ts b/src/data/vacuum.ts index 0ad0738d1b..9a5b963b86 100644 --- a/src/data/vacuum.ts +++ b/src/data/vacuum.ts @@ -2,21 +2,63 @@ import { HassEntityAttributeBase, HassEntityBase, } from "home-assistant-js-websocket"; +import { UNAVAILABLE } from "./entity"; -export const VACUUM_SUPPORT_PAUSE = 4; -export const VACUUM_SUPPORT_STOP = 8; -export const VACUUM_SUPPORT_RETURN_HOME = 16; -export const VACUUM_SUPPORT_FAN_SPEED = 32; -export const VACUUM_SUPPORT_BATTERY = 64; -export const VACUUM_SUPPORT_STATUS = 128; -export const VACUUM_SUPPORT_LOCATE = 512; -export const VACUUM_SUPPORT_CLEAN_SPOT = 1024; -export const VACUUM_SUPPORT_START = 8192; +export type VacuumEntityState = + | "on" + | "off" + | "cleaning" + | "docked" + | "idle" + | "paused" + | "returning" + | "error"; -export type VacuumEntity = HassEntityBase & { - attributes: HassEntityAttributeBase & { - battery_level: number; - fan_speed: any; - [key: string]: any; - }; -}; +export const enum VacuumEntityFeature { + TURN_ON = 1, + TURN_OFF = 2, + PAUSE = 4, + STOP = 8, + RETURN_HOME = 16, + FAN_SPEED = 32, + BATTERY = 64, + STATUS = 128, + SEND_COMMAND = 256, + LOCATE = 512, + CLEAN_SPOT = 1024, + MAP = 2048, + STATE = 4096, + START = 8192, +} + +interface VacuumEntityAttributes extends HassEntityAttributeBase { + battery_level?: number; + fan_speed?: any; + [key: string]: any; +} + +export interface VacuumEntity extends HassEntityBase { + attributes: VacuumEntityAttributes; +} + +export function isCleaning(stateObj: VacuumEntity): boolean { + return ["cleaning", "on"].includes(stateObj.state); +} + +export function canStart(stateObj: VacuumEntity): boolean { + if (stateObj.state === UNAVAILABLE) { + return false; + } + return !isCleaning(stateObj); +} + +export function canStop(stateObj: VacuumEntity): boolean { + return !["docked", "off"].includes(stateObj.state); +} + +export function canReturnHome(stateObj: VacuumEntity): boolean { + if (stateObj.state === UNAVAILABLE) { + return false; + } + return stateObj.state !== "returning"; +} diff --git a/src/dialogs/more-info/controls/more-info-vacuum.ts b/src/dialogs/more-info/controls/more-info-vacuum.ts index e53c4e9118..5a183712d6 100644 --- a/src/dialogs/more-info/controls/more-info-vacuum.ts +++ b/src/dialogs/more-info/controls/more-info-vacuum.ts @@ -18,18 +18,7 @@ import "../../../components/ha-icon"; import "../../../components/ha-icon-button"; import "../../../components/ha-select"; import { UNAVAILABLE } from "../../../data/entity"; -import { - VacuumEntity, - VACUUM_SUPPORT_BATTERY, - VACUUM_SUPPORT_CLEAN_SPOT, - VACUUM_SUPPORT_FAN_SPEED, - VACUUM_SUPPORT_LOCATE, - VACUUM_SUPPORT_PAUSE, - VACUUM_SUPPORT_RETURN_HOME, - VACUUM_SUPPORT_START, - VACUUM_SUPPORT_STATUS, - VACUUM_SUPPORT_STOP, -} from "../../../data/vacuum"; +import { VacuumEntity, VacuumEntityFeature } from "../../../data/vacuum"; import { HomeAssistant } from "../../../types"; interface VacuumCommand { @@ -44,7 +33,8 @@ const VACUUM_COMMANDS: VacuumCommand[] = [ translationKey: "start", icon: mdiPlay, serviceName: "start", - isVisible: (stateObj) => supportsFeature(stateObj, VACUUM_SUPPORT_START), + isVisible: (stateObj) => + supportsFeature(stateObj, VacuumEntityFeature.START), }, { translationKey: "pause", @@ -52,8 +42,8 @@ const VACUUM_COMMANDS: VacuumCommand[] = [ serviceName: "pause", isVisible: (stateObj) => // We need also to check if Start is supported because if not we show play-pause - supportsFeature(stateObj, VACUUM_SUPPORT_START) && - supportsFeature(stateObj, VACUUM_SUPPORT_PAUSE), + supportsFeature(stateObj, VacuumEntityFeature.START) && + supportsFeature(stateObj, VacuumEntityFeature.PAUSE), }, { translationKey: "start_pause", @@ -61,34 +51,36 @@ const VACUUM_COMMANDS: VacuumCommand[] = [ serviceName: "start_pause", isVisible: (stateObj) => // If start is supported, we don't show this button - !supportsFeature(stateObj, VACUUM_SUPPORT_START) && - supportsFeature(stateObj, VACUUM_SUPPORT_PAUSE), + !supportsFeature(stateObj, VacuumEntityFeature.START) && + supportsFeature(stateObj, VacuumEntityFeature.PAUSE), }, { translationKey: "stop", icon: mdiStop, serviceName: "stop", - isVisible: (stateObj) => supportsFeature(stateObj, VACUUM_SUPPORT_STOP), + isVisible: (stateObj) => + supportsFeature(stateObj, VacuumEntityFeature.STOP), }, { translationKey: "clean_spot", icon: mdiTargetVariant, serviceName: "clean_spot", isVisible: (stateObj) => - supportsFeature(stateObj, VACUUM_SUPPORT_CLEAN_SPOT), + supportsFeature(stateObj, VacuumEntityFeature.CLEAN_SPOT), }, { translationKey: "locate", icon: mdiMapMarker, serviceName: "locate", - isVisible: (stateObj) => supportsFeature(stateObj, VACUUM_SUPPORT_LOCATE), + isVisible: (stateObj) => + supportsFeature(stateObj, VacuumEntityFeature.LOCATE), }, { translationKey: "return_home", icon: mdiHomeMapMarker, serviceName: "return_to_base", isVisible: (stateObj) => - supportsFeature(stateObj, VACUUM_SUPPORT_RETURN_HOME), + supportsFeature(stateObj, VacuumEntityFeature.RETURN_HOME), }, ]; @@ -111,7 +103,7 @@ class MoreInfoVacuum extends LitElement { return html` ${stateObj.state !== UNAVAILABLE ? html`
- ${supportsFeature(stateObj, VACUUM_SUPPORT_STATUS) + ${supportsFeature(stateObj, VacuumEntityFeature.STATUS) ? html`
` : ""} - ${supportsFeature(stateObj, VACUUM_SUPPORT_BATTERY) && + ${supportsFeature(stateObj, VacuumEntityFeature.BATTERY) && stateObj.attributes.battery_level ? html`
@@ -177,7 +169,7 @@ class MoreInfoVacuum extends LitElement {
` : ""} - ${supportsFeature(stateObj, VACUUM_SUPPORT_FAN_SPEED) + ${supportsFeature(stateObj, VacuumEntityFeature.FAN_SPEED) ? html`
diff --git a/src/panels/lovelace/create-element/create-tile-extra-element.ts b/src/panels/lovelace/create-element/create-tile-extra-element.ts index e023cdc5f7..25321d149f 100644 --- a/src/panels/lovelace/create-element/create-tile-extra-element.ts +++ b/src/panels/lovelace/create-element/create-tile-extra-element.ts @@ -6,11 +6,13 @@ import { import "../tile-extra/hui-cover-open-close-tile-extra"; import "../tile-extra/hui-cover-tilt-tile-extra"; import "../tile-extra/hui-light-brightness-tile-extra"; +import "../tile-extra/hui-vacuum-commands-tile-extra"; const TYPES: Set = new Set([ "cover-open-close", "cover-tilt", "light-brightness", + "vacuum-commands", ]); export const createTileExtraElement = (config: LovelaceTileExtraConfig) => diff --git a/src/panels/lovelace/editor/config-elements/hui-tile-card-editor.ts b/src/panels/lovelace/editor/config-elements/hui-tile-card-editor.ts index e03e130296..7ac30fb4e6 100644 --- a/src/panels/lovelace/editor/config-elements/hui-tile-card-editor.ts +++ b/src/panels/lovelace/editor/config-elements/hui-tile-card-editor.ts @@ -20,7 +20,10 @@ import "../../../../components/ha-form/ha-form"; import type { SchemaUnion } from "../../../../components/ha-form/types"; import type { HomeAssistant } from "../../../../types"; import type { TileCardConfig } from "../../cards/types"; -import { LovelaceTileExtraConfig } from "../../tile-extra/types"; +import { + LovelaceTileExtraConfig, + LovelaceTileExtraContext, +} from "../../tile-extra/types"; import type { LovelaceCardEditor } from "../../types"; import "../hui-sub-element-editor"; import { actionConfigStruct } from "../structs/action-struct"; @@ -129,6 +132,10 @@ export class HuiTileCardEditor ] as const ); + private _context = memoizeOne( + (entity_id?: string): LovelaceTileExtraContext => ({ entity_id }) + ); + protected render(): TemplateResult { if (!this.hass || !this._config) { return html``; @@ -149,6 +156,7 @@ export class HuiTileCardEditor diff --git a/src/panels/lovelace/editor/config-elements/hui-tile-card-extras-editor.ts b/src/panels/lovelace/editor/config-elements/hui-tile-card-extras-editor.ts index bc5dbed4ac..24e75cf2aa 100644 --- a/src/panels/lovelace/editor/config-elements/hui-tile-card-extras-editor.ts +++ b/src/panels/lovelace/editor/config-elements/hui-tile-card-extras-editor.ts @@ -33,6 +33,7 @@ const EXTRAS_TYPE: LovelaceTileExtraConfig["type"][] = [ "cover-open-close", "cover-tilt", "light-brightness", + "vacuum-commands", ]; declare global { @@ -227,7 +228,7 @@ export class HuiTileCardExtrasEditor extends LitElement { let newExtra: LovelaceTileExtraConfig; if (elClass && elClass.getStubConfig) { - newExtra = await elClass.getStubConfig(this.hass!); + newExtra = await elClass.getStubConfig(this.hass!, this.stateObj); } else { newExtra = { type: value } as LovelaceTileExtraConfig; } diff --git a/src/panels/lovelace/editor/config-elements/hui-vacuum-commands-tile-extra-editor.ts b/src/panels/lovelace/editor/config-elements/hui-vacuum-commands-tile-extra-editor.ts new file mode 100644 index 0000000000..8613ac9dc9 --- /dev/null +++ b/src/panels/lovelace/editor/config-elements/hui-vacuum-commands-tile-extra-editor.ts @@ -0,0 +1,101 @@ +import { HassEntity } from "home-assistant-js-websocket"; +import { html, LitElement, TemplateResult } from "lit"; +import { customElement, property, state } from "lit/decorators"; +import memoizeOne from "memoize-one"; +import { fireEvent } from "../../../../common/dom/fire_event"; +import type { LocalizeFunc } from "../../../../common/translations/localize"; +import type { SchemaUnion } from "../../../../components/ha-form/types"; +import type { HomeAssistant } from "../../../../types"; +import { supportsVacuumCommand } from "../../tile-extra/hui-vacuum-commands-tile-extra"; +import { + LovelaceTileExtraContext, + VacuumCommandsTileExtraConfig, + VACUUM_COMMANDS, +} from "../../tile-extra/types"; +import type { LovelaceTileExtraEditor } from "../../types"; + +@customElement("hui-vacuum-commands-tile-extra-editor") +export class HuiVacuumCommandsTileExtraEditor + extends LitElement + implements LovelaceTileExtraEditor +{ + @property({ attribute: false }) public hass?: HomeAssistant; + + @property({ attribute: false }) public context?: LovelaceTileExtraContext; + + @state() private _config?: VacuumCommandsTileExtraConfig; + + public setConfig(config: VacuumCommandsTileExtraConfig): void { + this._config = config; + } + + private _schema = memoizeOne( + (localize: LocalizeFunc, stateObj?: HassEntity) => + [ + { + name: "commands", + selector: { + select: { + multiple: true, + options: VACUUM_COMMANDS.filter( + (command) => + stateObj && supportsVacuumCommand(stateObj, command) + ).map((command) => ({ + value: command, + label: `${localize( + `ui.panel.lovelace.editor.card.tile.extras.types.vacuum-commands.commands_list.${command}` + )}`, + })), + }, + }, + }, + ] as const + ); + + protected render(): TemplateResult { + if (!this.hass || !this._config) { + return html``; + } + + const stateObj = this.context?.entity_id + ? this.hass.states[this.context?.entity_id] + : undefined; + + const schema = this._schema(this.hass.localize, stateObj); + + return html` + + `; + } + + private _valueChanged(ev: CustomEvent): void { + fireEvent(this, "config-changed", { config: ev.detail.value }); + } + + private _computeLabelCallback = ( + schema: SchemaUnion> + ) => { + switch (schema.name) { + case "commands": + return this.hass!.localize( + `ui.panel.lovelace.editor.card.tile.extras.types.vacuum-commands.${schema.name}` + ); + default: + return this.hass!.localize( + `ui.panel.lovelace.editor.card.generic.${schema.name}` + ); + } + }; +} + +declare global { + interface HTMLElementTagNameMap { + "hui-vacuum-commands-tile-extra-editor": HuiVacuumCommandsTileExtraEditor; + } +} diff --git a/src/panels/lovelace/editor/hui-element-editor.ts b/src/panels/lovelace/editor/hui-element-editor.ts index bfa97f75bd..7bf4a80c6d 100644 --- a/src/panels/lovelace/editor/hui-element-editor.ts +++ b/src/panels/lovelace/editor/hui-element-editor.ts @@ -57,11 +57,13 @@ export interface UIConfigChangedEvent extends Event { }; } -export abstract class HuiElementEditor extends LitElement { +export abstract class HuiElementEditor extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; @property({ attribute: false }) public lovelace?: LovelaceConfig; + @property({ attribute: false }) public context?: C; + @state() private _yaml?: string; @state() private _config?: T; @@ -275,6 +277,9 @@ export abstract class HuiElementEditor extends LitElement { ) { this._configElement.lovelace = this.lovelace; } + if (this._configElement && changedProperties.has("context")) { + this._configElement.context = this.context; + } } private _handleUIConfigChanged(ev: UIConfigChangedEvent) { @@ -328,6 +333,7 @@ export abstract class HuiElementEditor extends LitElement { if ("lovelace" in configElement) { configElement.lovelace = this.lovelace; } + configElement.context = this.context; configElement.addEventListener("config-changed", (ev) => this._handleUIConfigChanged(ev as UIConfigChangedEvent) ); diff --git a/src/panels/lovelace/editor/hui-sub-element-editor.ts b/src/panels/lovelace/editor/hui-sub-element-editor.ts index 2d46415ffa..a8510541d6 100644 --- a/src/panels/lovelace/editor/hui-sub-element-editor.ts +++ b/src/panels/lovelace/editor/hui-sub-element-editor.ts @@ -25,6 +25,8 @@ export class HuiSubElementEditor extends LitElement { @property({ attribute: false }) public config!: SubElementEditorConfig; + @property({ attribute: false }) public context?: any; + @state() private _guiModeAvailable = true; @state() private _guiMode = true; @@ -66,6 +68,7 @@ export class HuiSubElementEditor extends LitElement { class="editor" .hass=${this.hass} .value=${this.config.elementConfig} + .context=${this.context} @config-changed=${this._handleConfigChanged} @GUImode-changed=${this._handleGUIModeChanged} > @@ -76,6 +79,7 @@ export class HuiSubElementEditor extends LitElement { class="editor" .hass=${this.hass} .value=${this.config.elementConfig} + .context=${this.context} @config-changed=${this._handleConfigChanged} @GUImode-changed=${this._handleGUIModeChanged} > @@ -86,6 +90,7 @@ export class HuiSubElementEditor extends LitElement { class="editor" .hass=${this.hass} .value=${this.config.elementConfig} + .context=${this.context} @config-changed=${this._handleConfigChanged} @GUImode-changed=${this._handleGUIModeChanged} > diff --git a/src/panels/lovelace/editor/tile-extra/hui-tile-extra-element-editor.ts b/src/panels/lovelace/editor/tile-extra/hui-tile-extra-element-editor.ts index 046a4d2d14..95300a78d2 100644 --- a/src/panels/lovelace/editor/tile-extra/hui-tile-extra-element-editor.ts +++ b/src/panels/lovelace/editor/tile-extra/hui-tile-extra-element-editor.ts @@ -1,11 +1,17 @@ import { customElement } from "lit/decorators"; import { getTileExtraElementClass } from "../../create-element/create-tile-extra-element"; -import { LovelaceTileExtraConfig } from "../../tile-extra/types"; +import { + LovelaceTileExtraConfig, + LovelaceTileExtraContext, +} from "../../tile-extra/types"; import type { LovelaceTileExtraEditor } from "../../types"; import { HuiElementEditor } from "../hui-element-editor"; @customElement("hui-tile-extra-element-editor") -export class HuiTileExtraElementEditor extends HuiElementEditor { +export class HuiTileExtraElementEditor extends HuiElementEditor< + LovelaceTileExtraConfig, + LovelaceTileExtraContext +> { protected async getConfigElement(): Promise< LovelaceTileExtraEditor | undefined > { diff --git a/src/panels/lovelace/tile-extra/hui-vacuum-commands-tile-extra.ts b/src/panels/lovelace/tile-extra/hui-vacuum-commands-tile-extra.ts new file mode 100644 index 0000000000..9a2c7e9191 --- /dev/null +++ b/src/panels/lovelace/tile-extra/hui-vacuum-commands-tile-extra.ts @@ -0,0 +1,220 @@ +import { + mdiHomeMapMarker, + mdiMapMarker, + mdiPause, + mdiPlay, + mdiPlayPause, + mdiStop, + mdiTargetVariant, +} from "@mdi/js"; +import { HassEntity } from "home-assistant-js-websocket"; +import { css, html, LitElement, TemplateResult } from "lit"; +import { customElement, property, state } from "lit/decorators"; +import { supportsFeature } from "../../../common/entity/supports-feature"; +import "../../../components/tile/ha-tile-button"; +import { UNAVAILABLE } from "../../../data/entity"; +import { + canReturnHome, + canStart, + canStop, + isCleaning, + VacuumEntity, + VacuumEntityFeature, +} from "../../../data/vacuum"; +import { HomeAssistant } from "../../../types"; +import { LovelaceTileExtra, LovelaceTileExtraEditor } from "../types"; +import { + VacuumCommand, + VacuumCommandsTileExtraConfig, + VACUUM_COMMANDS, +} from "./types"; + +interface VacuumButton { + translationKey: string; + icon: string; + serviceName: string; + disabled?: boolean; +} + +export const VACUUM_COMMANDS_FEATURES: Record< + VacuumCommand, + VacuumEntityFeature[] +> = { + start_pause: [VacuumEntityFeature.PAUSE], + stop: [VacuumEntityFeature.STOP], + clean_spot: [VacuumEntityFeature.CLEAN_SPOT], + locate: [VacuumEntityFeature.LOCATE], + return_home: [VacuumEntityFeature.RETURN_HOME], +}; + +export const supportsVacuumCommand = ( + stateObj: HassEntity, + command: VacuumCommand +): boolean => + VACUUM_COMMANDS_FEATURES[command].some((feature) => + supportsFeature(stateObj, feature) + ); + +export const VACUUM_COMMANDS_BUTTONS: Record< + VacuumCommand, + (stateObj: VacuumEntity) => VacuumButton +> = { + start_pause: (stateObj) => { + const startPauseOnly = + !supportsFeature(stateObj, VacuumEntityFeature.START) && + supportsFeature(stateObj, VacuumEntityFeature.PAUSE); + + if (startPauseOnly) { + return { + translationKey: "start_pause", + icon: mdiPlayPause, + serviceName: "start_pause", + }; + } + const canPause = + isCleaning(stateObj) && + supportsFeature(stateObj, VacuumEntityFeature.PAUSE); + + return canPause + ? { + translationKey: "pause", + icon: mdiPause, + serviceName: "pause", + } + : { + translationKey: "start", + icon: mdiPlay, + serviceName: "start", + disabled: canStart(stateObj), + }; + }, + stop: (stateObj) => ({ + translationKey: "stop", + icon: mdiStop, + serviceName: "stop", + disabled: !canStop(stateObj), + }), + clean_spot: () => ({ + translationKey: "clean_spot", + icon: mdiTargetVariant, + serviceName: "clean_spot", + }), + locate: () => ({ + translationKey: "locate", + icon: mdiMapMarker, + serviceName: "locate", + }), + return_home: (stateObj) => ({ + translationKey: "return_home", + icon: mdiHomeMapMarker, + serviceName: "return_to_base", + disabled: !canReturnHome(stateObj), + }), +}; + +@customElement("hui-vacuum-commands-tile-extra") +class HuiVacuumCommandTileExtra + extends LitElement + implements LovelaceTileExtra +{ + @property({ attribute: false }) public hass?: HomeAssistant; + + @property({ attribute: false }) public stateObj?: HassEntity; + + @state() private _config?: VacuumCommandsTileExtraConfig; + + static getStubConfig( + _, + stateObj?: HassEntity + ): VacuumCommandsTileExtraConfig { + return { + type: "vacuum-commands", + commands: stateObj + ? VACUUM_COMMANDS.filter((c) => + supportsVacuumCommand(stateObj, c) + ).slice(0, 3) + : [], + }; + } + + public static async getConfigElement(): Promise { + await import( + "../editor/config-elements/hui-vacuum-commands-tile-extra-editor" + ); + return document.createElement("hui-vacuum-commands-tile-extra-editor"); + } + + public setConfig(config: VacuumCommandsTileExtraConfig): void { + if (!config) { + throw new Error("Invalid configuration"); + } + this._config = config; + } + + private _onCommandTap(ev): void { + ev.stopPropagation(); + const entry = (ev.target! as any).entry as VacuumButton; + this.hass!.callService("vacuum", entry.serviceName, { + entity_id: this.stateObj!.entity_id, + }); + } + + protected render(): TemplateResult { + if (!this._config || !this.hass || !this.stateObj) { + return html``; + } + + const stateObj = this.stateObj as VacuumEntity; + + return html` +
+ ${VACUUM_COMMANDS.filter( + (command) => + supportsVacuumCommand(stateObj, command) && + this._config?.commands?.includes(command) + ).map((command) => { + const button = VACUUM_COMMANDS_BUTTONS[command](stateObj); + return html` + + + + `; + })} +
+ `; + } + + static get styles() { + return css` + .container { + display: flex; + flex-direction: row; + padding: 0 12px 12px 12px; + width: auto; + } + ha-tile-button { + flex: 1; + } + ha-tile-button:not(:last-child) { + margin-right: 12px; + margin-inline-end: 12px; + margin-inline-start: initial; + direction: var(--direction); + } + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + "hui-vacuum-commands-tile-extra": HuiVacuumCommandTileExtra; + } +} diff --git a/src/panels/lovelace/tile-extra/tile-extras.ts b/src/panels/lovelace/tile-extra/tile-extras.ts index be2029d086..2f8cb7d05b 100644 --- a/src/panels/lovelace/tile-extra/tile-extras.ts +++ b/src/panels/lovelace/tile-extra/tile-extras.ts @@ -3,7 +3,8 @@ import { computeDomain } from "../../../common/entity/compute_domain"; import { supportsFeature } from "../../../common/entity/supports-feature"; import { CoverEntityFeature } from "../../../data/cover"; import { lightSupportsBrightness } from "../../../data/light"; -import { LovelaceTileExtraConfig } from "./types"; +import { supportsVacuumCommand } from "./hui-vacuum-commands-tile-extra"; +import { LovelaceTileExtraConfig, VACUUM_COMMANDS } from "./types"; type TileExtraType = LovelaceTileExtraConfig["type"]; export type SupportsTileExtra = (stateObj: HassEntity) => boolean; @@ -20,9 +21,12 @@ const TILE_EXTRAS_SUPPORT: Record = { "light-brightness": (stateObj) => computeDomain(stateObj.entity_id) === "light" && lightSupportsBrightness(stateObj), + "vacuum-commands": (stateObj) => + computeDomain(stateObj.entity_id) === "vacuum" && + VACUUM_COMMANDS.some((c) => supportsVacuumCommand(stateObj, c)), }; -const TILE_EXTRAS_EDITABLE: Set = new Set([]); +const TILE_EXTRAS_EDITABLE: Set = new Set(["vacuum-commands"]); export const supportsTileExtra = ( stateObj: HassEntity, diff --git a/src/panels/lovelace/tile-extra/types.ts b/src/panels/lovelace/tile-extra/types.ts index 57d0897096..53d117f465 100644 --- a/src/panels/lovelace/tile-extra/types.ts +++ b/src/panels/lovelace/tile-extra/types.ts @@ -10,7 +10,27 @@ export interface LightBrightnessTileExtraConfig { type: "light-brightness"; } +export const VACUUM_COMMANDS = [ + "start_pause", + "stop", + "clean_spot", + "locate", + "return_home", +] as const; + +export type VacuumCommand = typeof VACUUM_COMMANDS[number]; + +export interface VacuumCommandsTileExtraConfig { + type: "vacuum-commands"; + commands?: VacuumCommand[]; +} + export type LovelaceTileExtraConfig = | CoverOpenCloseTileExtraConfig | CoverTiltTileExtraConfig - | LightBrightnessTileExtraConfig; + | LightBrightnessTileExtraConfig + | VacuumCommandsTileExtraConfig; + +export type LovelaceTileExtraContext = { + entity_id?: string; +}; diff --git a/src/panels/lovelace/types.ts b/src/panels/lovelace/types.ts index bb656818d5..55a39b536c 100644 --- a/src/panels/lovelace/types.ts +++ b/src/panels/lovelace/types.ts @@ -88,9 +88,10 @@ export interface LovelaceRowEditor extends LovelaceGenericElementEditor { setConfig(config: LovelaceRowConfig): void; } -export interface LovelaceGenericElementEditor extends HTMLElement { +export interface LovelaceGenericElementEditor extends HTMLElement { hass?: HomeAssistant; lovelace?: LovelaceConfig; + context?: C; setConfig(config: any): void; focusYamlEditor?: () => void; } @@ -104,7 +105,10 @@ export interface LovelaceTileExtra extends HTMLElement { export interface LovelaceTileExtraConstructor extends Constructor { getConfigElement?: () => LovelaceTileExtraEditor; - getStubConfig?: (hass: HomeAssistant) => LovelaceTileExtraConfig; + getStubConfig?: ( + hass: HomeAssistant, + stateObj?: HassEntity + ) => LovelaceTileExtraConfig; } export interface LovelaceTileExtraEditor extends LovelaceGenericElementEditor { diff --git a/src/translations/en.json b/src/translations/en.json index ea11f54a27..1ba64f531b 100755 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -4237,6 +4237,17 @@ }, "light-brightness": { "label": "Light brightness" + }, + "vacuum-commands": { + "label": "Vacuum commands", + "commands": "Commands", + "commands_list": { + "start_pause": "[%key:ui::dialogs::more_info_control::vacuum::start_pause%]", + "stop": "[%key:ui::dialogs::more_info_control::vacuum::stop%]", + "clean_spot": "[%key:ui::dialogs::more_info_control::vacuum::clean_spot%]", + "locate": "[%key:ui::dialogs::more_info_control::vacuum::locate%]", + "return_home": "[%key:ui::dialogs::more_info_control::vacuum::return_home%]" + } } } }