mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-28 03:36:44 +00:00
Add basic more info for lawn mower (#17601)
* Add basic more info for lawn mower * Change buttons layout
This commit is contained in:
parent
ac7c354bfc
commit
5f015ac9af
@ -11,10 +11,12 @@ export type LocalizeKeys =
|
|||||||
| `ui.card.alarm_control_panel.${string}`
|
| `ui.card.alarm_control_panel.${string}`
|
||||||
| `ui.card.weather.attributes.${string}`
|
| `ui.card.weather.attributes.${string}`
|
||||||
| `ui.card.weather.cardinal_direction.${string}`
|
| `ui.card.weather.cardinal_direction.${string}`
|
||||||
|
| `ui.card.lawn_mower.actions.${string}`
|
||||||
| `ui.components.calendar.event.rrule.${string}`
|
| `ui.components.calendar.event.rrule.${string}`
|
||||||
| `ui.components.logbook.${string}`
|
| `ui.components.logbook.${string}`
|
||||||
| `ui.components.selectors.file.${string}`
|
| `ui.components.selectors.file.${string}`
|
||||||
| `ui.dialogs.entity_registry.editor.${string}`
|
| `ui.dialogs.entity_registry.editor.${string}`
|
||||||
|
| `ui.dialogs.more_info_control.lawn_mower.${string}`
|
||||||
| `ui.dialogs.more_info_control.vacuum.${string}`
|
| `ui.dialogs.more_info_control.vacuum.${string}`
|
||||||
| `ui.dialogs.quick-bar.commands.${string}`
|
| `ui.dialogs.quick-bar.commands.${string}`
|
||||||
| `ui.dialogs.unhealthy.reason.${string}`
|
| `ui.dialogs.unhealthy.reason.${string}`
|
||||||
|
91
src/components/ha-lawn_mower-state.ts
Normal file
91
src/components/ha-lawn_mower-state.ts
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
import "@material/mwc-button";
|
||||||
|
import { CSSResultGroup, LitElement, css, html } from "lit";
|
||||||
|
import { customElement, property } from "lit/decorators";
|
||||||
|
import { supportsFeature } from "../common/entity/supports-feature";
|
||||||
|
import {
|
||||||
|
LawnMowerEntity,
|
||||||
|
LawnMowerEntityFeature,
|
||||||
|
LawnMowerEntityState,
|
||||||
|
} from "../data/lawn_mower";
|
||||||
|
import { HomeAssistant } from "../types";
|
||||||
|
|
||||||
|
type LawnMowerAction = {
|
||||||
|
action: string;
|
||||||
|
service: string;
|
||||||
|
feature: LawnMowerEntityFeature;
|
||||||
|
};
|
||||||
|
|
||||||
|
const LAWN_MOWER_ACTIONS: Partial<
|
||||||
|
Record<LawnMowerEntityState, LawnMowerAction>
|
||||||
|
> = {
|
||||||
|
mowing: {
|
||||||
|
action: "dock",
|
||||||
|
service: "dock",
|
||||||
|
feature: LawnMowerEntityFeature.DOCK,
|
||||||
|
},
|
||||||
|
docked: {
|
||||||
|
action: "start_mowing",
|
||||||
|
service: "start_mowing",
|
||||||
|
feature: LawnMowerEntityFeature.START_MOWING,
|
||||||
|
},
|
||||||
|
paused: {
|
||||||
|
action: "resume_mowing",
|
||||||
|
service: "start_mowing",
|
||||||
|
feature: LawnMowerEntityFeature.START_MOWING,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
@customElement("ha-lawn_mower-state")
|
||||||
|
class HaLawnMowerState extends LitElement {
|
||||||
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@property({ attribute: false }) public stateObj!: LawnMowerEntity;
|
||||||
|
|
||||||
|
public render() {
|
||||||
|
const state = this.stateObj.state;
|
||||||
|
const action = LAWN_MOWER_ACTIONS[state];
|
||||||
|
|
||||||
|
if (action && supportsFeature(this.stateObj, action.feature)) {
|
||||||
|
return html`
|
||||||
|
<mwc-button @click=${this.callService} .service=${action.service}>
|
||||||
|
${this.hass.localize(`ui.card.lawn_mower.actions.${action.action}`)}
|
||||||
|
</mwc-button>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<mwc-button disabled>
|
||||||
|
${this.hass.formatEntityState(this.stateObj)}
|
||||||
|
</mwc-button>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
callService(ev) {
|
||||||
|
ev.stopPropagation();
|
||||||
|
const stateObj = this.stateObj;
|
||||||
|
const service = ev.target.service;
|
||||||
|
this.hass.callService("lawn_mower", service, {
|
||||||
|
entity_id: stateObj.entity_id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResultGroup {
|
||||||
|
return css`
|
||||||
|
mwc-button {
|
||||||
|
top: 3px;
|
||||||
|
height: 37px;
|
||||||
|
margin-right: -0.57em;
|
||||||
|
}
|
||||||
|
mwc-button[disabled] {
|
||||||
|
background-color: transparent;
|
||||||
|
color: var(--secondary-text-color);
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ha-lawn_mower-state": HaLawnMowerState;
|
||||||
|
}
|
||||||
|
}
|
42
src/data/lawn_mower.ts
Normal file
42
src/data/lawn_mower.ts
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
import {
|
||||||
|
HassEntityAttributeBase,
|
||||||
|
HassEntityBase,
|
||||||
|
} from "home-assistant-js-websocket";
|
||||||
|
import { UNAVAILABLE } from "./entity";
|
||||||
|
|
||||||
|
export type LawnMowerEntityState = "paused" | "mowing" | "docked" | "error";
|
||||||
|
|
||||||
|
export const enum LawnMowerEntityFeature {
|
||||||
|
START_MOWING = 1,
|
||||||
|
PAUSE = 2,
|
||||||
|
DOCK = 4,
|
||||||
|
}
|
||||||
|
|
||||||
|
interface LawnMowerEntityAttributes extends HassEntityAttributeBase {
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LawnMowerEntity extends HassEntityBase {
|
||||||
|
attributes: LawnMowerEntityAttributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function canStartMowing(stateObj: LawnMowerEntity): boolean {
|
||||||
|
if (stateObj.state === UNAVAILABLE) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return stateObj.state !== "mowing";
|
||||||
|
}
|
||||||
|
|
||||||
|
export function canPause(stateObj: LawnMowerEntity): boolean {
|
||||||
|
if (stateObj.state === UNAVAILABLE) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return stateObj.state !== "paused";
|
||||||
|
}
|
||||||
|
|
||||||
|
export function canDock(stateObj: LawnMowerEntity): boolean {
|
||||||
|
if (stateObj.state === UNAVAILABLE) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return stateObj.state !== "docked";
|
||||||
|
}
|
@ -46,6 +46,7 @@ export const DOMAINS_WITH_MORE_INFO = [
|
|||||||
"image",
|
"image",
|
||||||
"input_boolean",
|
"input_boolean",
|
||||||
"input_datetime",
|
"input_datetime",
|
||||||
|
"lawn_mower",
|
||||||
"light",
|
"light",
|
||||||
"lock",
|
"lock",
|
||||||
"media_player",
|
"media_player",
|
||||||
|
213
src/dialogs/more-info/controls/more-info-lawn_mower.ts
Normal file
213
src/dialogs/more-info/controls/more-info-lawn_mower.ts
Normal file
@ -0,0 +1,213 @@
|
|||||||
|
import { mdiHomeMapMarker, mdiPause, mdiPlay } from "@mdi/js";
|
||||||
|
import { CSSResultGroup, LitElement, css, html, nothing } from "lit";
|
||||||
|
import { customElement, property } from "lit/decorators";
|
||||||
|
import memoizeOne from "memoize-one";
|
||||||
|
import { computeStateDisplay } from "../../../common/entity/compute_state_display";
|
||||||
|
import { computeStateDomain } from "../../../common/entity/compute_state_domain";
|
||||||
|
import { supportsFeature } from "../../../common/entity/supports-feature";
|
||||||
|
import { blankBeforePercent } from "../../../common/translations/blank_before_percent";
|
||||||
|
import "../../../components/entity/ha-battery-icon";
|
||||||
|
import "../../../components/ha-icon-button";
|
||||||
|
import { UNAVAILABLE } from "../../../data/entity";
|
||||||
|
import {
|
||||||
|
EntityRegistryDisplayEntry,
|
||||||
|
findBatteryChargingEntity,
|
||||||
|
findBatteryEntity,
|
||||||
|
} from "../../../data/entity_registry";
|
||||||
|
import {
|
||||||
|
LawnMowerEntity,
|
||||||
|
LawnMowerEntityFeature,
|
||||||
|
} from "../../../data/lawn_mower";
|
||||||
|
import { HomeAssistant } from "../../../types";
|
||||||
|
|
||||||
|
interface LawnMowerCommand {
|
||||||
|
translationKey: string;
|
||||||
|
icon: string;
|
||||||
|
serviceName: string;
|
||||||
|
isVisible: (stateObj: LawnMowerEntity) => boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const LAWN_MOWER_COMMANDS: LawnMowerCommand[] = [
|
||||||
|
{
|
||||||
|
translationKey: "start_mowing",
|
||||||
|
icon: mdiPlay,
|
||||||
|
serviceName: "start_mowing",
|
||||||
|
isVisible: (stateObj) =>
|
||||||
|
supportsFeature(stateObj, LawnMowerEntityFeature.START_MOWING),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
translationKey: "pause",
|
||||||
|
icon: mdiPause,
|
||||||
|
serviceName: "pause",
|
||||||
|
isVisible: (stateObj) =>
|
||||||
|
supportsFeature(stateObj, LawnMowerEntityFeature.PAUSE),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
translationKey: "dock",
|
||||||
|
icon: mdiHomeMapMarker,
|
||||||
|
serviceName: "dock",
|
||||||
|
isVisible: (stateObj) =>
|
||||||
|
supportsFeature(stateObj, LawnMowerEntityFeature.DOCK),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
@customElement("more-info-lawn_mower")
|
||||||
|
class MoreInfoLawnMower extends LitElement {
|
||||||
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@property() public stateObj?: LawnMowerEntity;
|
||||||
|
|
||||||
|
protected render() {
|
||||||
|
if (!this.hass || !this.stateObj) {
|
||||||
|
return nothing;
|
||||||
|
}
|
||||||
|
|
||||||
|
const stateObj = this.stateObj;
|
||||||
|
|
||||||
|
return html`
|
||||||
|
${stateObj.state !== UNAVAILABLE
|
||||||
|
? html` <div class="flex-horizontal">
|
||||||
|
<div>
|
||||||
|
<span class="status-subtitle"
|
||||||
|
>${this.hass!.localize(
|
||||||
|
"ui.dialogs.more_info_control.lawn_mower.activity"
|
||||||
|
)}:
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
<strong>
|
||||||
|
${computeStateDisplay(
|
||||||
|
this.hass.localize,
|
||||||
|
stateObj,
|
||||||
|
this.hass.locale,
|
||||||
|
this.hass.config,
|
||||||
|
this.hass.entities
|
||||||
|
)}
|
||||||
|
</strong>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
${this._renderBattery()}
|
||||||
|
</div>`
|
||||||
|
: nothing}
|
||||||
|
${LAWN_MOWER_COMMANDS.some((item) => item.isVisible(stateObj))
|
||||||
|
? html`
|
||||||
|
<div>
|
||||||
|
<p></p>
|
||||||
|
<div class="status-subtitle">
|
||||||
|
${this.hass!.localize(
|
||||||
|
"ui.dialogs.more_info_control.lawn_mower.commands"
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div class="flex-horizontal space-around">
|
||||||
|
${LAWN_MOWER_COMMANDS.filter((item) =>
|
||||||
|
item.isVisible(stateObj)
|
||||||
|
).map(
|
||||||
|
(item) => html`
|
||||||
|
<div>
|
||||||
|
<ha-icon-button
|
||||||
|
.path=${item.icon}
|
||||||
|
.entry=${item}
|
||||||
|
@click=${this.callService}
|
||||||
|
.label=${this.hass!.localize(
|
||||||
|
`ui.dialogs.more_info_control.lawn_mower.${item.translationKey}`
|
||||||
|
)}
|
||||||
|
.disabled=${stateObj.state === UNAVAILABLE}
|
||||||
|
></ha-icon-button>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _deviceEntities = memoizeOne(
|
||||||
|
(
|
||||||
|
deviceId: string,
|
||||||
|
entities: HomeAssistant["entities"]
|
||||||
|
): EntityRegistryDisplayEntry[] => {
|
||||||
|
const entries = Object.values(entities);
|
||||||
|
return entries.filter((entity) => entity.device_id === deviceId);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
private _renderBattery() {
|
||||||
|
const stateObj = this.stateObj!;
|
||||||
|
|
||||||
|
const deviceId = this.hass.entities[stateObj.entity_id]?.device_id;
|
||||||
|
|
||||||
|
const entities = deviceId
|
||||||
|
? this._deviceEntities(deviceId, this.hass.entities)
|
||||||
|
: [];
|
||||||
|
|
||||||
|
const batteryEntity = findBatteryEntity(this.hass, entities);
|
||||||
|
const battery = batteryEntity
|
||||||
|
? this.hass.states[batteryEntity.entity_id]
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
const batteryIsBinary =
|
||||||
|
battery && computeStateDomain(battery) === "binary_sensor";
|
||||||
|
|
||||||
|
// Use device battery entity
|
||||||
|
if (battery && (batteryIsBinary || !isNaN(battery.state as any))) {
|
||||||
|
const batteryChargingEntity = findBatteryChargingEntity(
|
||||||
|
this.hass,
|
||||||
|
entities
|
||||||
|
);
|
||||||
|
const batteryCharging = batteryChargingEntity
|
||||||
|
? this.hass.states[batteryChargingEntity?.entity_id]
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<div>
|
||||||
|
<span>
|
||||||
|
${batteryIsBinary
|
||||||
|
? ""
|
||||||
|
: `${Number(battery.state).toFixed()}${blankBeforePercent(
|
||||||
|
this.hass.locale
|
||||||
|
)}%`}
|
||||||
|
<ha-battery-icon
|
||||||
|
.hass=${this.hass}
|
||||||
|
.batteryStateObj=${battery}
|
||||||
|
.batteryChargingStateObj=${batteryCharging}
|
||||||
|
></ha-battery-icon>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nothing;
|
||||||
|
}
|
||||||
|
|
||||||
|
private callService(ev: CustomEvent) {
|
||||||
|
const entry = (ev.target! as any).entry as LawnMowerCommand;
|
||||||
|
this.hass.callService("lawn_mower", entry.serviceName, {
|
||||||
|
entity_id: this.stateObj!.entity_id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResultGroup {
|
||||||
|
return css`
|
||||||
|
:host {
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
.status-subtitle {
|
||||||
|
color: var(--secondary-text-color);
|
||||||
|
}
|
||||||
|
.flex-horizontal {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
.space-around {
|
||||||
|
justify-content: space-around;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"more-info-lawn_mower": MoreInfoLawnMower;
|
||||||
|
}
|
||||||
|
}
|
@ -21,6 +21,7 @@ const LAZY_LOADED_MORE_INFO_CONTROL = {
|
|||||||
image: () => import("./controls/more-info-image"),
|
image: () => import("./controls/more-info-image"),
|
||||||
input_boolean: () => import("./controls/more-info-input_boolean"),
|
input_boolean: () => import("./controls/more-info-input_boolean"),
|
||||||
input_datetime: () => import("./controls/more-info-input_datetime"),
|
input_datetime: () => import("./controls/more-info-input_datetime"),
|
||||||
|
lawn_mower: () => import("./controls/more-info-lawn_mower"),
|
||||||
light: () => import("./controls/more-info-light"),
|
light: () => import("./controls/more-info-light"),
|
||||||
lock: () => import("./controls/more-info-lock"),
|
lock: () => import("./controls/more-info-lock"),
|
||||||
media_player: () => import("./controls/more-info-media_player"),
|
media_player: () => import("./controls/more-info-media_player"),
|
||||||
|
@ -5,15 +5,16 @@ import { stateCardType } from "../common/entity/state_card_type";
|
|||||||
import "./state-card-alert";
|
import "./state-card-alert";
|
||||||
import "./state-card-button";
|
import "./state-card-button";
|
||||||
import "./state-card-climate";
|
import "./state-card-climate";
|
||||||
import "./state-card-humidifier";
|
|
||||||
import "./state-card-configurator";
|
import "./state-card-configurator";
|
||||||
import "./state-card-cover";
|
import "./state-card-cover";
|
||||||
import "./state-card-display";
|
import "./state-card-display";
|
||||||
import "./state-card-event";
|
import "./state-card-event";
|
||||||
|
import "./state-card-humidifier";
|
||||||
import "./state-card-input_button";
|
import "./state-card-input_button";
|
||||||
import "./state-card-input_number";
|
import "./state-card-input_number";
|
||||||
import "./state-card-input_select";
|
import "./state-card-input_select";
|
||||||
import "./state-card-input_text";
|
import "./state-card-input_text";
|
||||||
|
import "./state-card-lawn_mower";
|
||||||
import "./state-card-lock";
|
import "./state-card-lock";
|
||||||
import "./state-card-media_player";
|
import "./state-card-media_player";
|
||||||
import "./state-card-number";
|
import "./state-card-number";
|
||||||
|
43
src/state-summary/state-card-lawn_mower.ts
Normal file
43
src/state-summary/state-card-lawn_mower.ts
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
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 { haStyle } from "../resources/styles";
|
||||||
|
import type { HomeAssistant } from "../types";
|
||||||
|
|
||||||
|
@customElement("state-card-lawn_mower")
|
||||||
|
class StateCardLawnMower extends LitElement {
|
||||||
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@property() public stateObj!: HassEntity;
|
||||||
|
|
||||||
|
@property({ type: Boolean }) public inDialog = false;
|
||||||
|
|
||||||
|
public render() {
|
||||||
|
const stateObj = this.stateObj;
|
||||||
|
return html`
|
||||||
|
<div class="horizontal justified layout">
|
||||||
|
<state-info
|
||||||
|
.hass=${this.hass}
|
||||||
|
.stateObj=${stateObj}
|
||||||
|
.inDialog=${this.inDialog}
|
||||||
|
></state-info>
|
||||||
|
<ha-lawn_mower-state
|
||||||
|
.hass=${this.hass}
|
||||||
|
.stateObj=${stateObj}
|
||||||
|
></ha-lawn_mower-state>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResultGroup {
|
||||||
|
return haStyle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"state-card-lawn_mower": StateCardLawnMower;
|
||||||
|
}
|
||||||
|
}
|
@ -139,6 +139,13 @@
|
|||||||
"drying": "{name} drying",
|
"drying": "{name} drying",
|
||||||
"on_entity": "{name} on"
|
"on_entity": "{name} on"
|
||||||
},
|
},
|
||||||
|
"lawn_mower": {
|
||||||
|
"actions": {
|
||||||
|
"resume_mowing": "Resume mowing",
|
||||||
|
"start_mowing": "Start mowing",
|
||||||
|
"dock": "Return to dock"
|
||||||
|
}
|
||||||
|
},
|
||||||
"light": {
|
"light": {
|
||||||
"brightness": "Brightness",
|
"brightness": "Brightness",
|
||||||
"color_temperature": "Color temperature",
|
"color_temperature": "Color temperature",
|
||||||
@ -1006,6 +1013,13 @@
|
|||||||
"target_label": "[%key:ui::dialogs::more_info_control::climate::target_label%]",
|
"target_label": "[%key:ui::dialogs::more_info_control::climate::target_label%]",
|
||||||
"target": "[%key:ui::dialogs::more_info_control::climate::target%]"
|
"target": "[%key:ui::dialogs::more_info_control::climate::target%]"
|
||||||
},
|
},
|
||||||
|
"lawn_mower": {
|
||||||
|
"activity": "Activity",
|
||||||
|
"commands": "Lawn mower commands:",
|
||||||
|
"start_mowing": "Start mowing",
|
||||||
|
"pause": "Pause",
|
||||||
|
"dock": "Return to dock"
|
||||||
|
},
|
||||||
"water_heater": {
|
"water_heater": {
|
||||||
"target": "[%key:ui::dialogs::more_info_control::climate::target%]"
|
"target": "[%key:ui::dialogs::more_info_control::climate::target%]"
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user