diff --git a/src/data/vacuum.ts b/src/data/vacuum.ts
new file mode 100644
index 000000000000..0ad0738d1b4c
--- /dev/null
+++ b/src/data/vacuum.ts
@@ -0,0 +1,22 @@
+import {
+ HassEntityAttributeBase,
+ HassEntityBase,
+} from "home-assistant-js-websocket";
+
+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 VacuumEntity = HassEntityBase & {
+ attributes: HassEntityAttributeBase & {
+ battery_level: number;
+ fan_speed: any;
+ [key: string]: any;
+ };
+};
diff --git a/src/dialogs/more-info/controls/more-info-vacuum.js b/src/dialogs/more-info/controls/more-info-vacuum.js
deleted file mode 100644
index d7124671632b..000000000000
--- a/src/dialogs/more-info/controls/more-info-vacuum.js
+++ /dev/null
@@ -1,263 +0,0 @@
-import "@polymer/iron-flex-layout/iron-flex-layout-classes";
-import "@polymer/iron-icon/iron-icon";
-import "@polymer/paper-icon-button/paper-icon-button";
-import "@polymer/paper-item/paper-item";
-import "@polymer/paper-listbox/paper-listbox";
-import { html } from "@polymer/polymer/lib/utils/html-tag";
-import { PolymerElement } from "@polymer/polymer/polymer-element";
-
-import "../../../components/ha-attributes";
-import "../../../components/ha-paper-dropdown-menu";
-import { supportsFeature } from "../../../common/entity/supports-feature";
-
-class MoreInfoVacuum extends PolymerElement {
- static get template() {
- return html`
-
-
-
-
-
- Status: [[stateObj.attributes.status]]
-
-
-
- [[stateObj.attributes.battery_level]] %
-
-
-
-
-
Vacuum cleaner commands:
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- [[item]]
-
-
-
-
-
- [[stateObj.attributes.fan_speed]]
-
-
-
-
-
- `;
- }
-
- static get properties() {
- return {
- hass: {
- type: Object,
- },
-
- inDialog: {
- type: Boolean,
- value: false,
- },
-
- stateObj: {
- type: Object,
- },
- };
- }
-
- supportsPause(stateObj) {
- return supportsFeature(stateObj, 4);
- }
-
- supportsStop(stateObj) {
- return supportsFeature(stateObj, 8);
- }
-
- supportsReturnHome(stateObj) {
- return supportsFeature(stateObj, 16);
- }
-
- supportsFanSpeed(stateObj) {
- return supportsFeature(stateObj, 32);
- }
-
- supportsBattery(stateObj) {
- return supportsFeature(stateObj, 64);
- }
-
- supportsStatus(stateObj) {
- return supportsFeature(stateObj, 128);
- }
-
- supportsLocate(stateObj) {
- return supportsFeature(stateObj, 512);
- }
-
- supportsCleanSpot(stateObj) {
- return supportsFeature(stateObj, 1024);
- }
-
- supportsStart(stateObj) {
- return supportsFeature(stateObj, 8192);
- }
-
- supportsCommandBar(stateObj) {
- return (
- supportsFeature(stateObj, 4) |
- supportsFeature(stateObj, 8) |
- supportsFeature(stateObj, 16) |
- supportsFeature(stateObj, 512) |
- supportsFeature(stateObj, 1024) |
- supportsFeature(stateObj, 8192)
- );
- }
-
- fanSpeedChanged(ev) {
- var oldVal = this.stateObj.attributes.fan_speed;
- var newVal = ev.detail.value;
-
- if (!newVal || oldVal === newVal) return;
-
- this.hass.callService("vacuum", "set_fan_speed", {
- entity_id: this.stateObj.entity_id,
- fan_speed: newVal,
- });
- }
-
- onStop() {
- this.hass.callService("vacuum", "stop", {
- entity_id: this.stateObj.entity_id,
- });
- }
-
- onPlayPause() {
- this.hass.callService("vacuum", "start_pause", {
- entity_id: this.stateObj.entity_id,
- });
- }
-
- onPause() {
- this.hass.callService("vacuum", "pause", {
- entity_id: this.stateObj.entity_id,
- });
- }
-
- onStart() {
- this.hass.callService("vacuum", "start", {
- entity_id: this.stateObj.entity_id,
- });
- }
-
- onLocate() {
- this.hass.callService("vacuum", "locate", {
- entity_id: this.stateObj.entity_id,
- });
- }
-
- onCleanSpot() {
- this.hass.callService("vacuum", "clean_spot", {
- entity_id: this.stateObj.entity_id,
- });
- }
-
- onReturnHome() {
- this.hass.callService("vacuum", "return_to_base", {
- entity_id: this.stateObj.entity_id,
- });
- }
-}
-
-customElements.define("more-info-vacuum", MoreInfoVacuum);
diff --git a/src/dialogs/more-info/controls/more-info-vacuum.ts b/src/dialogs/more-info/controls/more-info-vacuum.ts
new file mode 100644
index 000000000000..4d6d1c362f68
--- /dev/null
+++ b/src/dialogs/more-info/controls/more-info-vacuum.ts
@@ -0,0 +1,256 @@
+import "@polymer/iron-icon/iron-icon";
+import "@polymer/paper-icon-button/paper-icon-button";
+import "@polymer/paper-item/paper-item";
+import "@polymer/paper-listbox/paper-listbox";
+
+import {
+ css,
+ CSSResult,
+ customElement,
+ html,
+ LitElement,
+ property,
+ TemplateResult,
+} from "lit-element";
+
+import { supportsFeature } from "../../../common/entity/supports-feature";
+import { HomeAssistant } from "../../../types";
+
+import "../../../components/ha-paper-dropdown-menu";
+import "../../../components/ha-attributes";
+import {
+ 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,
+ VacuumEntity,
+} from "../../../data/vacuum";
+
+interface VacuumCommand {
+ translationKey: string;
+ icon: string;
+ serviceName: string;
+ isVisible: (stateObj: VacuumEntity) => boolean;
+}
+
+const VACUUM_COMMANDS: VacuumCommand[] = [
+ {
+ translationKey: "start",
+ icon: "hass:play",
+ serviceName: "start",
+ isVisible: (stateObj) => supportsFeature(stateObj, VACUUM_SUPPORT_START),
+ },
+ {
+ translationKey: "pause",
+ icon: "hass:pause",
+ 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),
+ },
+ {
+ translationKey: "start_pause",
+ icon: "hass:play-pause",
+ 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),
+ },
+ {
+ translationKey: "stop",
+ icon: "hass:stop",
+ serviceName: "stop",
+ isVisible: (stateObj) => supportsFeature(stateObj, VACUUM_SUPPORT_STOP),
+ },
+ {
+ translationKey: "clean_spot",
+ icon: "hass:broom",
+ serviceName: "clean_spot",
+ isVisible: (stateObj) =>
+ supportsFeature(stateObj, VACUUM_SUPPORT_CLEAN_SPOT),
+ },
+ {
+ translationKey: "locate",
+ icon: "hass:map-marker",
+ serviceName: "locate",
+ isVisible: (stateObj) => supportsFeature(stateObj, VACUUM_SUPPORT_LOCATE),
+ },
+ {
+ translationKey: "return_home",
+ icon: "hass:home-map-marker",
+ serviceName: "return_to_base",
+ isVisible: (stateObj) =>
+ supportsFeature(stateObj, VACUUM_SUPPORT_RETURN_HOME),
+ },
+];
+
+@customElement("more-info-vacuum")
+class MoreInfoVacuum extends LitElement {
+ @property() public hass!: HomeAssistant;
+
+ @property() public stateObj?: VacuumEntity;
+
+ protected render(): TemplateResult {
+ if (!this.hass || !this.stateObj) {
+ return html``;
+ }
+
+ const stateObj = this.stateObj;
+
+ const filterExtraAttributes =
+ "fan_speed,fan_speed_list,status,battery_level,battery_icon";
+
+ return html`
+
+ ${supportsFeature(stateObj, VACUUM_SUPPORT_STATUS)
+ ? html`
+
+ ${this.hass!.localize(
+ "ui.dialogs.more_info_control.vacuum.status"
+ )}:
+
+ ${stateObj.attributes.status}
+
+ `
+ : ""}
+ ${supportsFeature(stateObj, VACUUM_SUPPORT_BATTERY)
+ ? html`
+
+
+
+ ${stateObj.attributes.battery_level} %
+
+
`
+ : ""}
+
+
+ ${VACUUM_COMMANDS.some((item) => item.isVisible(stateObj))
+ ? html`
+
+
+
+ ${this.hass!.localize(
+ "ui.dialogs.more_info_control.vacuum.commands"
+ )}
+
+
+ ${VACUUM_COMMANDS.filter((item) =>
+ item.isVisible(stateObj)
+ ).map(
+ (item) => html`
+
+ `
+ )}
+
+
+ `
+ : ""}
+ ${supportsFeature(stateObj, VACUUM_SUPPORT_FAN_SPEED)
+ ? html`
+
+
+
+
+ ${stateObj.attributes.fan_speed_list!.map(
+ (mode) => html`
+
+ ${mode}
+
+ `
+ )}
+
+
+
+
+
+ ${stateObj.attributes.fan_speed}
+
+
+
+
+
+ `
+ : ""}
+
+
+ `;
+ }
+
+ private callService(ev: CustomEvent) {
+ const entry = (ev.target! as any).entry as VacuumCommand;
+ this.hass.callService("vacuum", entry.serviceName, {
+ entity_id: this.stateObj!.entity_id,
+ });
+ }
+
+ private handleFanSpeedChanged(ev: CustomEvent) {
+ const oldVal = this.stateObj!.attributes.fan_speed;
+ const newVal = ev.detail.item.itemName;
+
+ if (!newVal || oldVal === newVal) {
+ return;
+ }
+
+ this.hass.callService("vacuum", "set_fan_speed", {
+ entity_id: this.stateObj!.entity_id,
+ fan_speed: newVal,
+ });
+ }
+
+ static get styles(): CSSResult {
+ return css`
+ :host {
+ @apply --paper-font-body1;
+ line-height: 1.5;
+ }
+ .status-subtitle {
+ color: var(--secondary-text-color);
+ }
+ paper-item {
+ cursor: pointer;
+ }
+ .flex-horizontal {
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ }
+ `;
+ }
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ "more-info-vacuum": MoreInfoVacuum;
+ }
+}
diff --git a/src/translations/en.json b/src/translations/en.json
index 74af9fff7e70..5c6f5e6da978 100755
--- a/src/translations/en.json
+++ b/src/translations/en.json
@@ -618,6 +618,18 @@
"remove_action": "Remove entity",
"confirm_remove_title": "Remove entity?",
"confirm_remove_text": "Are you sure you want to remove this entity?"
+ },
+ "vacuum": {
+ "status": "Status",
+ "commands": "Vacuum cleaner commands:",
+ "fan_speed": "Fan speed",
+ "start": "Start",
+ "pause": "Pause",
+ "stop": "Stop",
+ "clean_spot": "Clean spot",
+ "locate": "Locate",
+ "return_home": "Return home",
+ "start_pause": "Start/Pause"
}
},
"entity_registry": {