Tile feature lawn mower (#17655)

* Add lawn mower commands to tile card

* Rename state to action button
This commit is contained in:
Paul Bottein 2023-08-21 15:49:33 +02:00 committed by GitHub
parent bfcdbbd70b
commit eca3ec7f98
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 328 additions and 15 deletions

View File

@ -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;
}
}

View File

@ -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<LovelaceTileFeatureConfig["type"]> = new Set([
"light-brightness",
"light-color-temp",
"vacuum-commands",
"lawn-mower-commands",
"fan-speed",
"alarm-modes",
"climate-hvac-modes",

View File

@ -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`
<ha-form
.hass=${this.hass}
.data=${this._config}
.schema=${schema}
.computeLabel=${this._computeLabelCallback}
@value-changed=${this._valueChanged}
></ha-form>
`;
}
private _valueChanged(ev: CustomEvent): void {
fireEvent(this, "config-changed", { config: ev.detail.value });
}
private _computeLabelCallback = (
schema: SchemaUnion<ReturnType<typeof this._schema>>
) => {
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;
}
}

View File

@ -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<FeatureType>([
"alarm-modes",
"climate-hvac-modes",
"water-heater-operation-modes",
"lawn-mower-commands",
]);
const SUPPORTS_FEATURE_TYPES: Record<FeatureType, SupportsFeature | undefined> =
{
"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,
};

View File

@ -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<LovelaceTileFeatureEditor> {
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`
<ha-control-button-group>
${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`
<ha-control-button
.entry=${button}
.label=${this.hass!.localize(
// @ts-ignore
`ui.dialogs.more_info_control.lawn_mower.${button.translationKey}`
)}
@click=${this._onCommandTap}
.disabled=${button.disabled || stateObj.state === UNAVAILABLE}
>
<ha-svg-icon .path=${button.icon}></ha-svg-icon>
</ha-control-button>
`;
})}
</ha-control-button-group>
`;
}
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;
}
}

View File

@ -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 = {

View File

@ -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}
></state-info>
<ha-lawn_mower-state
<ha-lawn_mower-action-button
.hass=${this.hass}
.stateObj=${stateObj}
></ha-lawn_mower-state>
></ha-lawn_mower-action-button>
</div>
`;
}

View File

@ -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%]"
}
}
}
}