diff --git a/src/components/ha-lawn_mower-state.ts b/src/components/ha-lawn_mower-action-button.ts similarity index 93% rename from src/components/ha-lawn_mower-state.ts rename to src/components/ha-lawn_mower-action-button.ts index 8214ae1ee0..cdf3d35a5c 100644 --- a/src/components/ha-lawn_mower-state.ts +++ b/src/components/ha-lawn_mower-action-button.ts @@ -35,8 +35,8 @@ const LAWN_MOWER_ACTIONS: Partial< }, }; -@customElement("ha-lawn_mower-state") -class HaLawnMowerState extends LitElement { +@customElement("ha-lawn_mower-action-button") +class HaLawnMowerActionButton extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; @property({ attribute: false }) public stateObj!: LawnMowerEntity; @@ -86,6 +86,6 @@ class HaLawnMowerState extends LitElement { declare global { interface HTMLElementTagNameMap { - "ha-lawn_mower-state": HaLawnMowerState; + "ha-lawn_mower-action-button": HaLawnMowerActionButton; } } diff --git a/src/panels/lovelace/create-element/create-tile-feature-element.ts b/src/panels/lovelace/create-element/create-tile-feature-element.ts index 22aafff590..142838310b 100644 --- a/src/panels/lovelace/create-element/create-tile-feature-element.ts +++ b/src/panels/lovelace/create-element/create-tile-feature-element.ts @@ -7,6 +7,7 @@ import "../tile-features/hui-fan-speed-tile-feature"; import "../tile-features/hui-light-brightness-tile-feature"; import "../tile-features/hui-light-color-temp-tile-feature"; import "../tile-features/hui-vacuum-commands-tile-feature"; +import "../tile-features/hui-lawn-mower-commands-tile-feature"; import "../tile-features/hui-water-heater-operation-modes-tile-feature"; import { LovelaceTileFeatureConfig } from "../tile-features/types"; import { @@ -21,6 +22,7 @@ const TYPES: Set = new Set([ "light-brightness", "light-color-temp", "vacuum-commands", + "lawn-mower-commands", "fan-speed", "alarm-modes", "climate-hvac-modes", diff --git a/src/panels/lovelace/editor/config-elements/hui-lawn-mower-commands-tile-feature-editor.ts b/src/panels/lovelace/editor/config-elements/hui-lawn-mower-commands-tile-feature-editor.ts new file mode 100644 index 0000000000..64fc9387b5 --- /dev/null +++ b/src/panels/lovelace/editor/config-elements/hui-lawn-mower-commands-tile-feature-editor.ts @@ -0,0 +1,102 @@ +import { HassEntity } from "home-assistant-js-websocket"; +import { html, LitElement, nothing } 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 { supportsLawnMowerCommand } from "../../tile-features/hui-lawn-mower-commands-tile-feature"; +import { + LAWN_MOWER_COMMANDS, + LawnMowerCommandsTileFeatureConfig, + LovelaceTileFeatureContext, +} from "../../tile-features/types"; +import type { LovelaceTileFeatureEditor } from "../../types"; + +@customElement("hui-lawn-mower-commands-tile-feature-editor") +export class HuiLawnMowerCommandsTileFeatureEditor + extends LitElement + implements LovelaceTileFeatureEditor +{ + @property({ attribute: false }) public hass?: HomeAssistant; + + @property({ attribute: false }) public context?: LovelaceTileFeatureContext; + + @state() private _config?: LawnMowerCommandsTileFeatureConfig; + + public setConfig(config: LawnMowerCommandsTileFeatureConfig): void { + this._config = config; + } + + private _schema = memoizeOne( + (localize: LocalizeFunc, stateObj?: HassEntity) => + [ + { + name: "commands", + selector: { + select: { + multiple: true, + mode: "list", + options: LAWN_MOWER_COMMANDS.filter( + (command) => + stateObj && supportsLawnMowerCommand(stateObj, command) + ).map((command) => ({ + value: command, + label: `${localize( + `ui.panel.lovelace.editor.card.tile.features.types.lawn-mower-commands.commands_list.${command}` + )}`, + })), + }, + }, + }, + ] as const + ); + + protected render() { + if (!this.hass || !this._config) { + return nothing; + } + + 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.features.types.lawn-mower-commands.${schema.name}` + ); + default: + return this.hass!.localize( + `ui.panel.lovelace.editor.card.generic.${schema.name}` + ); + } + }; +} + +declare global { + interface HTMLElementTagNameMap { + "hui-lawn-mower-commands-tile-feature-editor": HuiLawnMowerCommandsTileFeatureEditor; + } +} diff --git a/src/panels/lovelace/editor/config-elements/hui-tile-card-features-editor.ts b/src/panels/lovelace/editor/config-elements/hui-tile-card-features-editor.ts index 1db19aa31f..12411d4092 100644 --- a/src/panels/lovelace/editor/config-elements/hui-tile-card-features-editor.ts +++ b/src/panels/lovelace/editor/config-elements/hui-tile-card-features-editor.ts @@ -31,6 +31,7 @@ import { supportsCoverOpenCloseTileFeature } from "../../tile-features/hui-cover import { supportsCoverPositionTileFeature } from "../../tile-features/hui-cover-position-tile-feature"; import { supportsCoverTiltTileFeature } from "../../tile-features/hui-cover-tilt-tile-feature"; import { supportsFanSpeedTileFeature } from "../../tile-features/hui-fan-speed-tile-feature"; +import { supportsLawnMowerCommandTileFeature } from "../../tile-features/hui-lawn-mower-commands-tile-feature"; import { supportsLightBrightnessTileFeature } from "../../tile-features/hui-light-brightness-tile-feature"; import { supportsLightColorTempTileFeature } from "../../tile-features/hui-light-color-temp-tile-feature"; import { supportsVacuumCommandTileFeature } from "../../tile-features/hui-vacuum-commands-tile-feature"; @@ -41,15 +42,16 @@ type FeatureType = LovelaceTileFeatureConfig["type"]; type SupportsFeature = (stateObj: HassEntity) => boolean; const FEATURE_TYPES: FeatureType[] = [ + "alarm-modes", + "climate-hvac-modes", "cover-open-close", "cover-position", "cover-tilt", + "fan-speed", + "lawn-mower-commands", "light-brightness", "light-color-temp", "vacuum-commands", - "fan-speed", - "alarm-modes", - "climate-hvac-modes", "water-heater-operation-modes", ]; @@ -58,19 +60,21 @@ const EDITABLES_FEATURE_TYPES = new Set([ "alarm-modes", "climate-hvac-modes", "water-heater-operation-modes", + "lawn-mower-commands", ]); const SUPPORTS_FEATURE_TYPES: Record = { + "alarm-modes": supportsAlarmModesTileFeature, + "climate-hvac-modes": supportsClimateHvacModesTileFeature, "cover-open-close": supportsCoverOpenCloseTileFeature, "cover-position": supportsCoverPositionTileFeature, "cover-tilt": supportsCoverTiltTileFeature, + "fan-speed": supportsFanSpeedTileFeature, + "lawn-mower-commands": supportsLawnMowerCommandTileFeature, "light-brightness": supportsLightBrightnessTileFeature, "light-color-temp": supportsLightColorTempTileFeature, "vacuum-commands": supportsVacuumCommandTileFeature, - "fan-speed": supportsFanSpeedTileFeature, - "alarm-modes": supportsAlarmModesTileFeature, - "climate-hvac-modes": supportsClimateHvacModesTileFeature, "water-heater-operation-modes": supportsWaterHeaterOperationModesTileFeature, }; diff --git a/src/panels/lovelace/tile-features/hui-lawn-mower-commands-tile-feature.ts b/src/panels/lovelace/tile-features/hui-lawn-mower-commands-tile-feature.ts new file mode 100644 index 0000000000..d1b508e2ad --- /dev/null +++ b/src/panels/lovelace/tile-features/hui-lawn-mower-commands-tile-feature.ts @@ -0,0 +1,187 @@ +import { mdiHomeMapMarker, mdiPause, mdiPlay } from "@mdi/js"; +import { HassEntity } from "home-assistant-js-websocket"; +import { LitElement, css, html, nothing } from "lit"; +import { customElement, property, state } from "lit/decorators"; +import { computeDomain } from "../../../common/entity/compute_domain"; +import { supportsFeature } from "../../../common/entity/supports-feature"; +import "../../../components/ha-control-button"; +import "../../../components/ha-control-button-group"; +import { UNAVAILABLE } from "../../../data/entity"; +import { + LawnMowerEntity, + LawnMowerEntityFeature, + canDock, +} from "../../../data/lawn_mower"; +import { HomeAssistant } from "../../../types"; +import { LovelaceTileFeature, LovelaceTileFeatureEditor } from "../types"; +import { + LAWN_MOWER_COMMANDS, + LawnMowerCommand, + LawnMowerCommandsTileFeatureConfig, +} from "./types"; + +interface LawnMowerButton { + translationKey: string; + icon: string; + serviceName: string; + disabled?: boolean; +} + +export const LAWN_MOWER_COMMANDS_FEATURES: Record< + LawnMowerCommand, + LawnMowerEntityFeature[] +> = { + start_pause: [ + LawnMowerEntityFeature.PAUSE, + LawnMowerEntityFeature.START_MOWING, + ], + dock: [LawnMowerEntityFeature.DOCK], +}; + +export const supportsLawnMowerCommand = ( + stateObj: HassEntity, + command: LawnMowerCommand +): boolean => + LAWN_MOWER_COMMANDS_FEATURES[command].some((feature) => + supportsFeature(stateObj, feature) + ); + +export const LAWN_MOWER_COMMANDS_BUTTONS: Record< + LawnMowerCommand, + (stateObj: LawnMowerEntity) => LawnMowerButton +> = { + start_pause: (stateObj) => { + const canPause = + stateObj.state === "mowing" && + supportsFeature(stateObj, LawnMowerEntityFeature.PAUSE); + + return canPause + ? { + translationKey: "pause", + icon: mdiPause, + serviceName: "pause", + } + : { + translationKey: "start", + icon: mdiPlay, + serviceName: "start_mowing", + }; + }, + dock: (stateObj) => ({ + translationKey: "dock", + icon: mdiHomeMapMarker, + serviceName: "dock", + disabled: !canDock(stateObj), + }), +}; + +export const supportsLawnMowerCommandTileFeature = (stateObj: HassEntity) => { + const domain = computeDomain(stateObj.entity_id); + return ( + domain === "lawn_mower" && + LAWN_MOWER_COMMANDS.some((c) => supportsLawnMowerCommand(stateObj, c)) + ); +}; + +@customElement("hui-lawn-mower-commands-tile-feature") +class HuiLawnMowerCommandTileFeature + extends LitElement + implements LovelaceTileFeature +{ + @property({ attribute: false }) public hass?: HomeAssistant; + + @property({ attribute: false }) public stateObj?: HassEntity; + + @state() private _config?: LawnMowerCommandsTileFeatureConfig; + + static getStubConfig( + _, + stateObj?: HassEntity + ): LawnMowerCommandsTileFeatureConfig { + return { + type: "lawn-mower-commands", + commands: stateObj + ? LAWN_MOWER_COMMANDS.filter((c) => + supportsLawnMowerCommand(stateObj, c) + ).slice(0, 3) + : [], + }; + } + + public static async getConfigElement(): Promise { + await import( + "../editor/config-elements/hui-lawn-mower-commands-tile-feature-editor" + ); + return document.createElement( + "hui-lawn-mower-commands-tile-feature-editor" + ); + } + + public setConfig(config: LawnMowerCommandsTileFeatureConfig): 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 LawnMowerButton; + this.hass!.callService("lawn_mower", entry.serviceName, { + entity_id: this.stateObj!.entity_id, + }); + } + + protected render() { + if ( + !this._config || + !this.hass || + !this.stateObj || + !supportsLawnMowerCommandTileFeature(this.stateObj) + ) { + return nothing; + } + + const stateObj = this.stateObj as LawnMowerEntity; + + return html` + + ${LAWN_MOWER_COMMANDS.filter( + (command) => + supportsLawnMowerCommand(stateObj, command) && + this._config?.commands?.includes(command) + ).map((command) => { + const button = LAWN_MOWER_COMMANDS_BUTTONS[command](stateObj); + return html` + + + + `; + })} + + `; + } + + static get styles() { + return css` + ha-control-button-group { + margin: 0 12px 12px 12px; + --control-button-group-spacing: 12px; + } + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + "hui-lawn-mower-commands-tile-feature": HuiLawnMowerCommandTileFeature; + } +} diff --git a/src/panels/lovelace/tile-features/types.ts b/src/panels/lovelace/tile-features/types.ts index 79b202ad8f..848862c4d3 100644 --- a/src/panels/lovelace/tile-features/types.ts +++ b/src/panels/lovelace/tile-features/types.ts @@ -56,16 +56,26 @@ export interface VacuumCommandsTileFeatureConfig { commands?: VacuumCommand[]; } +export const LAWN_MOWER_COMMANDS = ["start_pause", "dock"] as const; + +export type LawnMowerCommand = (typeof LAWN_MOWER_COMMANDS)[number]; + +export interface LawnMowerCommandsTileFeatureConfig { + type: "lawn-mower-commands"; + commands?: LawnMowerCommand[]; +} + export type LovelaceTileFeatureConfig = + | AlarmModesTileFeatureConfig + | ClimateHvacModesTileFeatureConfig | CoverOpenCloseTileFeatureConfig | CoverPositionTileFeatureConfig | CoverTiltTileFeatureConfig + | FanSpeedTileFeatureConfig + | LawnMowerCommandsTileFeatureConfig | LightBrightnessTileFeatureConfig | LightColorTempTileFeatureConfig | VacuumCommandsTileFeatureConfig - | FanSpeedTileFeatureConfig - | AlarmModesTileFeatureConfig - | ClimateHvacModesTileFeatureConfig | WaterHeaterOperationModesTileFeatureConfig; export type LovelaceTileFeatureContext = { diff --git a/src/state-summary/state-card-lawn_mower.ts b/src/state-summary/state-card-lawn_mower.ts index afa8c570a2..a9cb5a17b8 100644 --- a/src/state-summary/state-card-lawn_mower.ts +++ b/src/state-summary/state-card-lawn_mower.ts @@ -2,7 +2,7 @@ import { HassEntity } from "home-assistant-js-websocket"; import { CSSResultGroup, LitElement, html } from "lit"; import { customElement, property } from "lit/decorators"; import "../components/entity/state-info"; -import "../components/ha-lawn_mower-state"; +import "../components/ha-lawn_mower-action-button"; import { haStyle } from "../resources/styles"; import type { HomeAssistant } from "../types"; @@ -23,10 +23,10 @@ class StateCardLawnMower extends LitElement { .stateObj=${stateObj} .inDialog=${this.inDialog} > - + > `; } diff --git a/src/translations/en.json b/src/translations/en.json index 042d14deff..3bf9d6582a 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -5026,6 +5026,14 @@ "water-heater-operation-modes": { "label": "Water heater operation modes", "operation_modes": "Operation modes" + }, + "lawn-mower-commands": { + "label": "Lawn mower commands", + "commands": "Commands", + "commands_list": { + "start_pause": "Start Pause", + "dock": "[%key:ui::dialogs::more_info_control::lawn_mower::dock%]" + } } } }