mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-27 19:26:36 +00:00
And vacuum commands extra for tile card (#14434)
This commit is contained in:
parent
566b93ec1f
commit
fcfdad3d94
@ -2,21 +2,63 @@ import {
|
|||||||
HassEntityAttributeBase,
|
HassEntityAttributeBase,
|
||||||
HassEntityBase,
|
HassEntityBase,
|
||||||
} from "home-assistant-js-websocket";
|
} from "home-assistant-js-websocket";
|
||||||
|
import { UNAVAILABLE } from "./entity";
|
||||||
|
|
||||||
export const VACUUM_SUPPORT_PAUSE = 4;
|
export type VacuumEntityState =
|
||||||
export const VACUUM_SUPPORT_STOP = 8;
|
| "on"
|
||||||
export const VACUUM_SUPPORT_RETURN_HOME = 16;
|
| "off"
|
||||||
export const VACUUM_SUPPORT_FAN_SPEED = 32;
|
| "cleaning"
|
||||||
export const VACUUM_SUPPORT_BATTERY = 64;
|
| "docked"
|
||||||
export const VACUUM_SUPPORT_STATUS = 128;
|
| "idle"
|
||||||
export const VACUUM_SUPPORT_LOCATE = 512;
|
| "paused"
|
||||||
export const VACUUM_SUPPORT_CLEAN_SPOT = 1024;
|
| "returning"
|
||||||
export const VACUUM_SUPPORT_START = 8192;
|
| "error";
|
||||||
|
|
||||||
export type VacuumEntity = HassEntityBase & {
|
export const enum VacuumEntityFeature {
|
||||||
attributes: HassEntityAttributeBase & {
|
TURN_ON = 1,
|
||||||
battery_level: number;
|
TURN_OFF = 2,
|
||||||
fan_speed: any;
|
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;
|
[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";
|
||||||
|
}
|
||||||
|
@ -18,18 +18,7 @@ import "../../../components/ha-icon";
|
|||||||
import "../../../components/ha-icon-button";
|
import "../../../components/ha-icon-button";
|
||||||
import "../../../components/ha-select";
|
import "../../../components/ha-select";
|
||||||
import { UNAVAILABLE } from "../../../data/entity";
|
import { UNAVAILABLE } from "../../../data/entity";
|
||||||
import {
|
import { VacuumEntity, VacuumEntityFeature } from "../../../data/vacuum";
|
||||||
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 { HomeAssistant } from "../../../types";
|
import { HomeAssistant } from "../../../types";
|
||||||
|
|
||||||
interface VacuumCommand {
|
interface VacuumCommand {
|
||||||
@ -44,7 +33,8 @@ const VACUUM_COMMANDS: VacuumCommand[] = [
|
|||||||
translationKey: "start",
|
translationKey: "start",
|
||||||
icon: mdiPlay,
|
icon: mdiPlay,
|
||||||
serviceName: "start",
|
serviceName: "start",
|
||||||
isVisible: (stateObj) => supportsFeature(stateObj, VACUUM_SUPPORT_START),
|
isVisible: (stateObj) =>
|
||||||
|
supportsFeature(stateObj, VacuumEntityFeature.START),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
translationKey: "pause",
|
translationKey: "pause",
|
||||||
@ -52,8 +42,8 @@ const VACUUM_COMMANDS: VacuumCommand[] = [
|
|||||||
serviceName: "pause",
|
serviceName: "pause",
|
||||||
isVisible: (stateObj) =>
|
isVisible: (stateObj) =>
|
||||||
// We need also to check if Start is supported because if not we show play-pause
|
// We need also to check if Start is supported because if not we show play-pause
|
||||||
supportsFeature(stateObj, VACUUM_SUPPORT_START) &&
|
supportsFeature(stateObj, VacuumEntityFeature.START) &&
|
||||||
supportsFeature(stateObj, VACUUM_SUPPORT_PAUSE),
|
supportsFeature(stateObj, VacuumEntityFeature.PAUSE),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
translationKey: "start_pause",
|
translationKey: "start_pause",
|
||||||
@ -61,34 +51,36 @@ const VACUUM_COMMANDS: VacuumCommand[] = [
|
|||||||
serviceName: "start_pause",
|
serviceName: "start_pause",
|
||||||
isVisible: (stateObj) =>
|
isVisible: (stateObj) =>
|
||||||
// If start is supported, we don't show this button
|
// If start is supported, we don't show this button
|
||||||
!supportsFeature(stateObj, VACUUM_SUPPORT_START) &&
|
!supportsFeature(stateObj, VacuumEntityFeature.START) &&
|
||||||
supportsFeature(stateObj, VACUUM_SUPPORT_PAUSE),
|
supportsFeature(stateObj, VacuumEntityFeature.PAUSE),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
translationKey: "stop",
|
translationKey: "stop",
|
||||||
icon: mdiStop,
|
icon: mdiStop,
|
||||||
serviceName: "stop",
|
serviceName: "stop",
|
||||||
isVisible: (stateObj) => supportsFeature(stateObj, VACUUM_SUPPORT_STOP),
|
isVisible: (stateObj) =>
|
||||||
|
supportsFeature(stateObj, VacuumEntityFeature.STOP),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
translationKey: "clean_spot",
|
translationKey: "clean_spot",
|
||||||
icon: mdiTargetVariant,
|
icon: mdiTargetVariant,
|
||||||
serviceName: "clean_spot",
|
serviceName: "clean_spot",
|
||||||
isVisible: (stateObj) =>
|
isVisible: (stateObj) =>
|
||||||
supportsFeature(stateObj, VACUUM_SUPPORT_CLEAN_SPOT),
|
supportsFeature(stateObj, VacuumEntityFeature.CLEAN_SPOT),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
translationKey: "locate",
|
translationKey: "locate",
|
||||||
icon: mdiMapMarker,
|
icon: mdiMapMarker,
|
||||||
serviceName: "locate",
|
serviceName: "locate",
|
||||||
isVisible: (stateObj) => supportsFeature(stateObj, VACUUM_SUPPORT_LOCATE),
|
isVisible: (stateObj) =>
|
||||||
|
supportsFeature(stateObj, VacuumEntityFeature.LOCATE),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
translationKey: "return_home",
|
translationKey: "return_home",
|
||||||
icon: mdiHomeMapMarker,
|
icon: mdiHomeMapMarker,
|
||||||
serviceName: "return_to_base",
|
serviceName: "return_to_base",
|
||||||
isVisible: (stateObj) =>
|
isVisible: (stateObj) =>
|
||||||
supportsFeature(stateObj, VACUUM_SUPPORT_RETURN_HOME),
|
supportsFeature(stateObj, VacuumEntityFeature.RETURN_HOME),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -111,7 +103,7 @@ class MoreInfoVacuum extends LitElement {
|
|||||||
return html`
|
return html`
|
||||||
${stateObj.state !== UNAVAILABLE
|
${stateObj.state !== UNAVAILABLE
|
||||||
? html` <div class="flex-horizontal">
|
? html` <div class="flex-horizontal">
|
||||||
${supportsFeature(stateObj, VACUUM_SUPPORT_STATUS)
|
${supportsFeature(stateObj, VacuumEntityFeature.STATUS)
|
||||||
? html`
|
? html`
|
||||||
<div>
|
<div>
|
||||||
<span class="status-subtitle"
|
<span class="status-subtitle"
|
||||||
@ -131,7 +123,7 @@ class MoreInfoVacuum extends LitElement {
|
|||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
${supportsFeature(stateObj, VACUUM_SUPPORT_BATTERY) &&
|
${supportsFeature(stateObj, VacuumEntityFeature.BATTERY) &&
|
||||||
stateObj.attributes.battery_level
|
stateObj.attributes.battery_level
|
||||||
? html`
|
? html`
|
||||||
<div>
|
<div>
|
||||||
@ -177,7 +169,7 @@ class MoreInfoVacuum extends LitElement {
|
|||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
${supportsFeature(stateObj, VACUUM_SUPPORT_FAN_SPEED)
|
${supportsFeature(stateObj, VacuumEntityFeature.FAN_SPEED)
|
||||||
? html`
|
? html`
|
||||||
<div>
|
<div>
|
||||||
<div class="flex-horizontal">
|
<div class="flex-horizontal">
|
||||||
|
@ -6,11 +6,13 @@ import {
|
|||||||
import "../tile-extra/hui-cover-open-close-tile-extra";
|
import "../tile-extra/hui-cover-open-close-tile-extra";
|
||||||
import "../tile-extra/hui-cover-tilt-tile-extra";
|
import "../tile-extra/hui-cover-tilt-tile-extra";
|
||||||
import "../tile-extra/hui-light-brightness-tile-extra";
|
import "../tile-extra/hui-light-brightness-tile-extra";
|
||||||
|
import "../tile-extra/hui-vacuum-commands-tile-extra";
|
||||||
|
|
||||||
const TYPES: Set<LovelaceTileExtraConfig["type"]> = new Set([
|
const TYPES: Set<LovelaceTileExtraConfig["type"]> = new Set([
|
||||||
"cover-open-close",
|
"cover-open-close",
|
||||||
"cover-tilt",
|
"cover-tilt",
|
||||||
"light-brightness",
|
"light-brightness",
|
||||||
|
"vacuum-commands",
|
||||||
]);
|
]);
|
||||||
|
|
||||||
export const createTileExtraElement = (config: LovelaceTileExtraConfig) =>
|
export const createTileExtraElement = (config: LovelaceTileExtraConfig) =>
|
||||||
|
@ -20,7 +20,10 @@ import "../../../../components/ha-form/ha-form";
|
|||||||
import type { SchemaUnion } from "../../../../components/ha-form/types";
|
import type { SchemaUnion } from "../../../../components/ha-form/types";
|
||||||
import type { HomeAssistant } from "../../../../types";
|
import type { HomeAssistant } from "../../../../types";
|
||||||
import type { TileCardConfig } from "../../cards/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 type { LovelaceCardEditor } from "../../types";
|
||||||
import "../hui-sub-element-editor";
|
import "../hui-sub-element-editor";
|
||||||
import { actionConfigStruct } from "../structs/action-struct";
|
import { actionConfigStruct } from "../structs/action-struct";
|
||||||
@ -129,6 +132,10 @@ export class HuiTileCardEditor
|
|||||||
] as const
|
] as const
|
||||||
);
|
);
|
||||||
|
|
||||||
|
private _context = memoizeOne(
|
||||||
|
(entity_id?: string): LovelaceTileExtraContext => ({ entity_id })
|
||||||
|
);
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
if (!this.hass || !this._config) {
|
if (!this.hass || !this._config) {
|
||||||
return html``;
|
return html``;
|
||||||
@ -149,6 +156,7 @@ export class HuiTileCardEditor
|
|||||||
<hui-sub-element-editor
|
<hui-sub-element-editor
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.config=${this._subElementEditorConfig}
|
.config=${this._subElementEditorConfig}
|
||||||
|
.context=${this._context(this._config.entity)}
|
||||||
@go-back=${this._goBack}
|
@go-back=${this._goBack}
|
||||||
@config-changed=${this.subElementChanged}
|
@config-changed=${this.subElementChanged}
|
||||||
>
|
>
|
||||||
|
@ -33,6 +33,7 @@ const EXTRAS_TYPE: LovelaceTileExtraConfig["type"][] = [
|
|||||||
"cover-open-close",
|
"cover-open-close",
|
||||||
"cover-tilt",
|
"cover-tilt",
|
||||||
"light-brightness",
|
"light-brightness",
|
||||||
|
"vacuum-commands",
|
||||||
];
|
];
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
@ -227,7 +228,7 @@ export class HuiTileCardExtrasEditor extends LitElement {
|
|||||||
|
|
||||||
let newExtra: LovelaceTileExtraConfig;
|
let newExtra: LovelaceTileExtraConfig;
|
||||||
if (elClass && elClass.getStubConfig) {
|
if (elClass && elClass.getStubConfig) {
|
||||||
newExtra = await elClass.getStubConfig(this.hass!);
|
newExtra = await elClass.getStubConfig(this.hass!, this.stateObj);
|
||||||
} else {
|
} else {
|
||||||
newExtra = { type: value } as LovelaceTileExtraConfig;
|
newExtra = { type: value } as LovelaceTileExtraConfig;
|
||||||
}
|
}
|
||||||
|
@ -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`
|
||||||
|
<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.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;
|
||||||
|
}
|
||||||
|
}
|
@ -57,11 +57,13 @@ export interface UIConfigChangedEvent extends Event {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export abstract class HuiElementEditor<T> extends LitElement {
|
export abstract class HuiElementEditor<T, C = any> extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
@property({ attribute: false }) public lovelace?: LovelaceConfig;
|
@property({ attribute: false }) public lovelace?: LovelaceConfig;
|
||||||
|
|
||||||
|
@property({ attribute: false }) public context?: C;
|
||||||
|
|
||||||
@state() private _yaml?: string;
|
@state() private _yaml?: string;
|
||||||
|
|
||||||
@state() private _config?: T;
|
@state() private _config?: T;
|
||||||
@ -275,6 +277,9 @@ export abstract class HuiElementEditor<T> extends LitElement {
|
|||||||
) {
|
) {
|
||||||
this._configElement.lovelace = this.lovelace;
|
this._configElement.lovelace = this.lovelace;
|
||||||
}
|
}
|
||||||
|
if (this._configElement && changedProperties.has("context")) {
|
||||||
|
this._configElement.context = this.context;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _handleUIConfigChanged(ev: UIConfigChangedEvent) {
|
private _handleUIConfigChanged(ev: UIConfigChangedEvent) {
|
||||||
@ -328,6 +333,7 @@ export abstract class HuiElementEditor<T> extends LitElement {
|
|||||||
if ("lovelace" in configElement) {
|
if ("lovelace" in configElement) {
|
||||||
configElement.lovelace = this.lovelace;
|
configElement.lovelace = this.lovelace;
|
||||||
}
|
}
|
||||||
|
configElement.context = this.context;
|
||||||
configElement.addEventListener("config-changed", (ev) =>
|
configElement.addEventListener("config-changed", (ev) =>
|
||||||
this._handleUIConfigChanged(ev as UIConfigChangedEvent)
|
this._handleUIConfigChanged(ev as UIConfigChangedEvent)
|
||||||
);
|
);
|
||||||
|
@ -25,6 +25,8 @@ export class HuiSubElementEditor extends LitElement {
|
|||||||
|
|
||||||
@property({ attribute: false }) public config!: SubElementEditorConfig;
|
@property({ attribute: false }) public config!: SubElementEditorConfig;
|
||||||
|
|
||||||
|
@property({ attribute: false }) public context?: any;
|
||||||
|
|
||||||
@state() private _guiModeAvailable = true;
|
@state() private _guiModeAvailable = true;
|
||||||
|
|
||||||
@state() private _guiMode = true;
|
@state() private _guiMode = true;
|
||||||
@ -66,6 +68,7 @@ export class HuiSubElementEditor extends LitElement {
|
|||||||
class="editor"
|
class="editor"
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.value=${this.config.elementConfig}
|
.value=${this.config.elementConfig}
|
||||||
|
.context=${this.context}
|
||||||
@config-changed=${this._handleConfigChanged}
|
@config-changed=${this._handleConfigChanged}
|
||||||
@GUImode-changed=${this._handleGUIModeChanged}
|
@GUImode-changed=${this._handleGUIModeChanged}
|
||||||
></hui-row-element-editor>
|
></hui-row-element-editor>
|
||||||
@ -76,6 +79,7 @@ export class HuiSubElementEditor extends LitElement {
|
|||||||
class="editor"
|
class="editor"
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.value=${this.config.elementConfig}
|
.value=${this.config.elementConfig}
|
||||||
|
.context=${this.context}
|
||||||
@config-changed=${this._handleConfigChanged}
|
@config-changed=${this._handleConfigChanged}
|
||||||
@GUImode-changed=${this._handleGUIModeChanged}
|
@GUImode-changed=${this._handleGUIModeChanged}
|
||||||
></hui-headerfooter-element-editor>
|
></hui-headerfooter-element-editor>
|
||||||
@ -86,6 +90,7 @@ export class HuiSubElementEditor extends LitElement {
|
|||||||
class="editor"
|
class="editor"
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.value=${this.config.elementConfig}
|
.value=${this.config.elementConfig}
|
||||||
|
.context=${this.context}
|
||||||
@config-changed=${this._handleConfigChanged}
|
@config-changed=${this._handleConfigChanged}
|
||||||
@GUImode-changed=${this._handleGUIModeChanged}
|
@GUImode-changed=${this._handleGUIModeChanged}
|
||||||
></hui-tile-extra-element-editor>
|
></hui-tile-extra-element-editor>
|
||||||
|
@ -1,11 +1,17 @@
|
|||||||
import { customElement } from "lit/decorators";
|
import { customElement } from "lit/decorators";
|
||||||
import { getTileExtraElementClass } from "../../create-element/create-tile-extra-element";
|
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 type { LovelaceTileExtraEditor } from "../../types";
|
||||||
import { HuiElementEditor } from "../hui-element-editor";
|
import { HuiElementEditor } from "../hui-element-editor";
|
||||||
|
|
||||||
@customElement("hui-tile-extra-element-editor")
|
@customElement("hui-tile-extra-element-editor")
|
||||||
export class HuiTileExtraElementEditor extends HuiElementEditor<LovelaceTileExtraConfig> {
|
export class HuiTileExtraElementEditor extends HuiElementEditor<
|
||||||
|
LovelaceTileExtraConfig,
|
||||||
|
LovelaceTileExtraContext
|
||||||
|
> {
|
||||||
protected async getConfigElement(): Promise<
|
protected async getConfigElement(): Promise<
|
||||||
LovelaceTileExtraEditor | undefined
|
LovelaceTileExtraEditor | undefined
|
||||||
> {
|
> {
|
||||||
|
220
src/panels/lovelace/tile-extra/hui-vacuum-commands-tile-extra.ts
Normal file
220
src/panels/lovelace/tile-extra/hui-vacuum-commands-tile-extra.ts
Normal file
@ -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<LovelaceTileExtraEditor> {
|
||||||
|
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`
|
||||||
|
<div class="container">
|
||||||
|
${VACUUM_COMMANDS.filter(
|
||||||
|
(command) =>
|
||||||
|
supportsVacuumCommand(stateObj, command) &&
|
||||||
|
this._config?.commands?.includes(command)
|
||||||
|
).map((command) => {
|
||||||
|
const button = VACUUM_COMMANDS_BUTTONS[command](stateObj);
|
||||||
|
return html`
|
||||||
|
<ha-tile-button
|
||||||
|
.entry=${button}
|
||||||
|
.label=${this.hass!.localize(
|
||||||
|
// @ts-ignore
|
||||||
|
`ui.dialogs.more_info_control.vacuum.${button.translationKey}`
|
||||||
|
)}
|
||||||
|
@click=${this._onCommandTap}
|
||||||
|
.disabled=${button.disabled || stateObj.state === UNAVAILABLE}
|
||||||
|
>
|
||||||
|
<ha-svg-icon .path=${button.icon}></ha-svg-icon>
|
||||||
|
</ha-tile-button>
|
||||||
|
`;
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
@ -3,7 +3,8 @@ import { computeDomain } from "../../../common/entity/compute_domain";
|
|||||||
import { supportsFeature } from "../../../common/entity/supports-feature";
|
import { supportsFeature } from "../../../common/entity/supports-feature";
|
||||||
import { CoverEntityFeature } from "../../../data/cover";
|
import { CoverEntityFeature } from "../../../data/cover";
|
||||||
import { lightSupportsBrightness } from "../../../data/light";
|
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"];
|
type TileExtraType = LovelaceTileExtraConfig["type"];
|
||||||
export type SupportsTileExtra = (stateObj: HassEntity) => boolean;
|
export type SupportsTileExtra = (stateObj: HassEntity) => boolean;
|
||||||
@ -20,9 +21,12 @@ const TILE_EXTRAS_SUPPORT: Record<TileExtraType, SupportsTileExtra> = {
|
|||||||
"light-brightness": (stateObj) =>
|
"light-brightness": (stateObj) =>
|
||||||
computeDomain(stateObj.entity_id) === "light" &&
|
computeDomain(stateObj.entity_id) === "light" &&
|
||||||
lightSupportsBrightness(stateObj),
|
lightSupportsBrightness(stateObj),
|
||||||
|
"vacuum-commands": (stateObj) =>
|
||||||
|
computeDomain(stateObj.entity_id) === "vacuum" &&
|
||||||
|
VACUUM_COMMANDS.some((c) => supportsVacuumCommand(stateObj, c)),
|
||||||
};
|
};
|
||||||
|
|
||||||
const TILE_EXTRAS_EDITABLE: Set<TileExtraType> = new Set([]);
|
const TILE_EXTRAS_EDITABLE: Set<TileExtraType> = new Set(["vacuum-commands"]);
|
||||||
|
|
||||||
export const supportsTileExtra = (
|
export const supportsTileExtra = (
|
||||||
stateObj: HassEntity,
|
stateObj: HassEntity,
|
||||||
|
@ -10,7 +10,27 @@ export interface LightBrightnessTileExtraConfig {
|
|||||||
type: "light-brightness";
|
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 =
|
export type LovelaceTileExtraConfig =
|
||||||
| CoverOpenCloseTileExtraConfig
|
| CoverOpenCloseTileExtraConfig
|
||||||
| CoverTiltTileExtraConfig
|
| CoverTiltTileExtraConfig
|
||||||
| LightBrightnessTileExtraConfig;
|
| LightBrightnessTileExtraConfig
|
||||||
|
| VacuumCommandsTileExtraConfig;
|
||||||
|
|
||||||
|
export type LovelaceTileExtraContext = {
|
||||||
|
entity_id?: string;
|
||||||
|
};
|
||||||
|
@ -88,9 +88,10 @@ export interface LovelaceRowEditor extends LovelaceGenericElementEditor {
|
|||||||
setConfig(config: LovelaceRowConfig): void;
|
setConfig(config: LovelaceRowConfig): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface LovelaceGenericElementEditor extends HTMLElement {
|
export interface LovelaceGenericElementEditor<C = any> extends HTMLElement {
|
||||||
hass?: HomeAssistant;
|
hass?: HomeAssistant;
|
||||||
lovelace?: LovelaceConfig;
|
lovelace?: LovelaceConfig;
|
||||||
|
context?: C;
|
||||||
setConfig(config: any): void;
|
setConfig(config: any): void;
|
||||||
focusYamlEditor?: () => void;
|
focusYamlEditor?: () => void;
|
||||||
}
|
}
|
||||||
@ -104,7 +105,10 @@ export interface LovelaceTileExtra extends HTMLElement {
|
|||||||
export interface LovelaceTileExtraConstructor
|
export interface LovelaceTileExtraConstructor
|
||||||
extends Constructor<LovelaceTileExtra> {
|
extends Constructor<LovelaceTileExtra> {
|
||||||
getConfigElement?: () => LovelaceTileExtraEditor;
|
getConfigElement?: () => LovelaceTileExtraEditor;
|
||||||
getStubConfig?: (hass: HomeAssistant) => LovelaceTileExtraConfig;
|
getStubConfig?: (
|
||||||
|
hass: HomeAssistant,
|
||||||
|
stateObj?: HassEntity
|
||||||
|
) => LovelaceTileExtraConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface LovelaceTileExtraEditor extends LovelaceGenericElementEditor {
|
export interface LovelaceTileExtraEditor extends LovelaceGenericElementEditor {
|
||||||
|
@ -4237,6 +4237,17 @@
|
|||||||
},
|
},
|
||||||
"light-brightness": {
|
"light-brightness": {
|
||||||
"label": "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%]"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user