diff --git a/README.md b/README.md index 78a915da..fb29d5f7 100644 --- a/README.md +++ b/README.md @@ -58,13 +58,19 @@ This card can be configured using Lovelace UI editor. 5. Choose `entity`. 6. Now you should see the preview of the card! -_Sorry, no support for `actions` and `stats` in visual config yet._ +_Sorry, no support for `actions`, `shortcuts` and `stats` in visual config yet._ Typical example of using this card in YAML config would look like this: ```yaml type: 'custom:vacuum-card' entity: vacuum.vacuum_cleaner +actions: + start: + service: xiaomi_miio.vacuum_clean_segment + service_data: + entity_id: vacuum.vacuum_cleaner + segments: [16, 20] stats: default: - attribute: filter_left @@ -86,7 +92,7 @@ stats: - attribute: cleaning_time unit: minutes subtitle: Cleaning time -actions: +shortcuts: - name: Clean living room service: script.clean_living_room icon: 'mdi:sofa' @@ -100,19 +106,20 @@ actions: Here is what every option means: -| Name | Type | Default | Description | -| -------------- | :-------: | ------------ | ----------------------------------------------------------------------- | -| `type` | `string` | **Required** | `custom:vacuum-card` | -| `entity` | `string` | **Required** | An entity_id within the `vacuum` domain. | -| `map` | `string` | Optional | An entity_id within the `camera` domain, for streaming live vacuum map. | -| `map_refresh` | `integer` | `5` | Update interval for map camera in seconds | -| `image` | `string` | `default` | Path to image of your vacuum cleaner. Better to have `png` or `svg`. | -| `show_name` | `boolean` | `true` | Show friendly name of the vacuum. | -| `show_status` | `boolean` | `true` | Show status of the vacuum. | -| `show_toolbar` | `boolean` | `true` | Show toolbar with actions. | -| `compact_view` | `boolean` | `false` | Compact view without image. | -| `stats` | `object` | Optional | Custom per state stats for your vacuum cleaner | -| `actions` | `object` | Optional | Custom actions for your vacuum cleaner. | +| Name | Type | Default | Description | +| -------------- | :-------: | ------------ | --------------------------------------------------------------------------------------------------------- | +| `type` | `string` | **Required** | `custom:vacuum-card` | +| `entity` | `string` | **Required** | An entity_id within the `vacuum` domain. | +| `map` | `string` | Optional | An entity_id within the `camera` domain, for streaming live vacuum map. | +| `map_refresh` | `integer` | `5` | Update interval for map camera in seconds | +| `image` | `string` | `default` | Path to image of your vacuum cleaner. Better to have `png` or `svg`. | +| `show_name` | `boolean` | `true` | Show friendly name of the vacuum. | +| `show_status` | `boolean` | `true` | Show status of the vacuum. | +| `show_toolbar` | `boolean` | `true` | Show toolbar with actions. | +| `compact_view` | `boolean` | `false` | Compact view without image. | +| `stats` | `object` | Optional | Custom per state stats for your vacuum cleaner | +| `actions` | `object` | Optional | Override default actions behavior with service invocations. | +| `shortcuts` | `object` | Optional | List of shortcuts shown at the right bottom part of the card with custom actions for your vacuum cleaner. | ### `stats` object @@ -127,7 +134,16 @@ You can use any attribute of vacuum or even any entity by `entity_id` to display ### `actions` object -You can defined [custom scripts][ha-scripts] for custom actions i.e cleaning specific room and add them to this card with `actions` option. +You can defined service invocations to override default actions behavior. Available actions to override are `start`, `pause`, `resume`, `stop`, `locate` and `return_to_base`. + +| Name | Type | Default | Description | +| -------------- | :------: | --------------------------------- | ----------------------------------------------- | +| `service` | `string` | Optional | A service to call, i.e. `script.clean_bedroom`. | +| `service_data` | `object` | `service_data` for `service` call | + +### `shortcuts` object + +You can defined [custom scripts][ha-scripts] for custom actions i.e cleaning specific room and add them to this card with `shortcuts` option. | Name | Type | Default | Description | | -------------- | :------: | --------------------------------- | -------------------------------------------------- | diff --git a/src/localize.js b/src/localize.js index 175dd268..9277847f 100644 --- a/src/localize.js +++ b/src/localize.js @@ -50,7 +50,7 @@ var languages = { vi, lt, ro, - pt + pt, }; const DEFAULT_LANG = 'en'; diff --git a/src/translations/cs.json b/src/translations/cs.json index 8f7ccbf7..e1b7dd1a 100644 --- a/src/translations/cs.json +++ b/src/translations/cs.json @@ -27,6 +27,9 @@ "error": { "missing_entity": "Je vyžadováno specifikování entity!" }, + "warning": { + "actions_array": "" + }, "editor": { "entity": "Entita (Povinný)", "map": "Mapa (Nepovinný)", diff --git a/src/translations/da.json b/src/translations/da.json index 1a643b5b..ced68335 100644 --- a/src/translations/da.json +++ b/src/translations/da.json @@ -27,6 +27,9 @@ "error": { "missing_entity": "En enhed skal specificeres!" }, + "warning": { + "actions_array": "" + }, "editor": { "entity": "Enhed (Påkrævet)", "map": "Map Camera (Valgfrit)", diff --git a/src/translations/de.json b/src/translations/de.json index 5cfeb80e..06209331 100644 --- a/src/translations/de.json +++ b/src/translations/de.json @@ -29,6 +29,9 @@ "error": { "missing_entity": "Angabe der Entität ist erforderlich!" }, + "warning": { + "actions_array": "" + }, "editor": { "entity": "Entität (Erforderlich)", "map": "Map Camera (Optional)", diff --git a/src/translations/en.json b/src/translations/en.json index 42d15a29..d58ec6df 100755 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -39,6 +39,9 @@ "error": { "missing_entity": "Specifying entity is required!" }, + "warning": { + "actions_array": "WARNING: 'actions' is reserved to override default actions for existing buttons. If your intention was to add additional actions, use the 'shortcuts' option instead." + }, "editor": { "entity": "Entity (Required)", "map": "Map Camera (Optional)", diff --git a/src/translations/es.json b/src/translations/es.json index 0ade1861..6b874012 100644 --- a/src/translations/es.json +++ b/src/translations/es.json @@ -29,6 +29,9 @@ "error": { "missing_entity": "¡Se requiere especificar una entidad!" }, + "warning": { + "actions_array": "ATENCIÓN: La opcion 'actions' está reservada para sobreescribir el comportamiento por defecto de los botones existentes. Si su intención es añadir acciones adicionales, debe utilizar la opcion 'shortcuts' en su lugar." + }, "editor": { "entity": "Entidad (Requerido)", "map": "Map Camera (Opcional)", diff --git a/src/translations/fr.json b/src/translations/fr.json index 9701e08d..c9c2e69d 100644 --- a/src/translations/fr.json +++ b/src/translations/fr.json @@ -38,6 +38,9 @@ "error": { "missing_entity": "La spécification de l'entité est requise !" }, + "warning": { + "actions_array": "" + }, "editor": { "entity": "Entité (obligatoire)", "map": "Caméra de carte (facultatif)", diff --git a/src/translations/he.json b/src/translations/he.json index 3d06e823..53ae3808 100644 --- a/src/translations/he.json +++ b/src/translations/he.json @@ -41,6 +41,9 @@ "error": { "missing_entity": "יש צורך לציין ישות!" }, + "warning": { + "actions_array": "" + }, "editor": { "entity": "ישות (נדרש)", "map": "מצלמת מפה (אפשרי)", diff --git a/src/translations/hu.json b/src/translations/hu.json index e872180b..132bebea 100644 --- a/src/translations/hu.json +++ b/src/translations/hu.json @@ -27,6 +27,9 @@ "error": { "missing_entity": "Entitás megadása kötelező!" }, + "warning": { + "actions_array": "" + }, "editor": { "entity": "Entitás (Kötelező)", "map": "Térkép kamera (Opcionális)", diff --git a/src/translations/it.json b/src/translations/it.json index baa9e4ff..8def67a3 100644 --- a/src/translations/it.json +++ b/src/translations/it.json @@ -19,6 +19,9 @@ "error": { "missing_entity": "È necessario specificare l'entità!" }, + "warning": { + "actions_array": "" + }, "editor": { "entity": "Entità (Richiesto)", "map": "Mappa (Opzionale)", diff --git a/src/translations/ko.json b/src/translations/ko.json index 50db33fc..47033732 100644 --- a/src/translations/ko.json +++ b/src/translations/ko.json @@ -27,6 +27,9 @@ "error": { "missing_entity": "구성요소를 선택해주세요." }, + "warning": { + "actions_array": "" + }, "editor": { "entity": "구성요소 (필수 요소)", "map": "지도 (선택 사항)", diff --git a/src/translations/nb.json b/src/translations/nb.json index 8fc894c2..f4408ef3 100644 --- a/src/translations/nb.json +++ b/src/translations/nb.json @@ -27,6 +27,9 @@ "error": { "missing_entity": "Spesifiserende enhet kreves!" }, + "warning": { + "actions_array": "" + }, "editor": { "entity": "Enhet (påkrevd)", "map": "Kartkamera (valgfritt)", diff --git a/src/translations/nl.json b/src/translations/nl.json index 46bd299c..7fdf0bb0 100644 --- a/src/translations/nl.json +++ b/src/translations/nl.json @@ -19,6 +19,9 @@ "error": { "missing_entity": "Het specificeren van een entiteit is verplicht!" }, + "warning": { + "actions_array": "" + }, "editor": { "entity": "Entiteit (Verplicht)", "map": "Kaart Camera (Optioneel)", diff --git a/src/translations/pl.json b/src/translations/pl.json index 8d83cd17..350ff640 100644 --- a/src/translations/pl.json +++ b/src/translations/pl.json @@ -19,6 +19,9 @@ "error": { "missing_entity": "Ustawienie encji jest wymagane!" }, + "warning": { + "actions_array": "" + }, "editor": { "entity": "Encja (wymagane)", "map": "Kamera (opcjonalne)", diff --git a/src/translations/pt.json b/src/translations/pt.json index 7f926219..ad489939 100644 --- a/src/translations/pt.json +++ b/src/translations/pt.json @@ -27,6 +27,9 @@ "error": { "missing_entity": "Entidade obrigatória!" }, + "warning": { + "actions_array": "" + }, "editor": { "entity": "Entidade (Obrigatório)", "map": "Mapa (Opcional)", diff --git a/src/translations/ro.json b/src/translations/ro.json index 15341394..9ac94622 100644 --- a/src/translations/ro.json +++ b/src/translations/ro.json @@ -1,61 +1,60 @@ { - "status": { - "cleaning": "Curățare", - "auto": "Curățare Automată", - "spot": "Curățare Punct", - "edge": "Curățare Margini", - "single_room": "Curățare o singură cameră", - "paused": "Repauz", - "idle": "Inactiv", - "stop": "Oprit", - "charging": "Încărcare", - "returning home": "Revenire Acasă", - "returning": "Revenire Acasă", - "docked": "Parcat", - "unknown": "Necunoscut", - "offline": "Deconectat", - "error": "Eroare" - }, - "source": { - "gentle": "Blând", - "silent": "Silențios", - "standard": "Standard", - "medium": "Mediu", - "turbo": "Turbo", - "normal": "Normal", - "high": "Ridicat" - }, - "common": { - "name": "Card de vid.", - "description": "Un card de vid vă permite să controlați vidul robotului.", - "start": "Curat", - "continue": "Continuă", - "pause": "Repauz", - "stop": "Stop", - "return_to_base": "Parchează", - "locate": "Găsește Aspirator", - "not_available": "Aspiratorul nu este disponibil" - }, - "error": { - "missing_entity": "Este necesară specificarea entității!" - }, - "editor": { - "entity": "Entitate (Necesar)", - "map": "Camera Harta (Optional)", - "image": "Imagine (Optional)", - "compact_view": "Vizualizare compactă", - "compact_view_aria_label_on": "Pornește vizualizare compactă", - "compact_view_aria_label_off": "Oprește vizualizare compactă compact view off", - "show_name": "Arată Nume", - "show_name_aria_label_on": "Pornește arată nume", - "show_name_aria_label_off": "Oprește arată nume", - "show_status": "Arată Status", - "show_status_aria_label_on": "Pornește arată status", - "show_status_aria_label_off": "Oprește arată status", - "show_toolbar": "Arată bara de instrumente", - "show_toolbar_aria_label_on": "Pornește arată bara de instrumente", - "show_toolbar_aria_label_off": "Oprește arată bara de instrumente", - "code_only_note": "Notă: Acțiunile de setare și opțiunile de statistici sunt disponibile exclusiv folosind Editorul de cod." - } + "status": { + "cleaning": "Curățare", + "auto": "Curățare Automată", + "spot": "Curățare Punct", + "edge": "Curățare Margini", + "single_room": "Curățare o singură cameră", + "paused": "Repauz", + "idle": "Inactiv", + "stop": "Oprit", + "charging": "Încărcare", + "returning home": "Revenire Acasă", + "returning": "Revenire Acasă", + "docked": "Parcat", + "unknown": "Necunoscut", + "offline": "Deconectat", + "error": "Eroare" + }, + "source": { + "gentle": "Blând", + "silent": "Silențios", + "standard": "Standard", + "medium": "Mediu", + "turbo": "Turbo", + "normal": "Normal", + "high": "Ridicat" + }, + "common": { + "name": "Card de vid.", + "description": "Un card de vid vă permite să controlați vidul robotului.", + "start": "Curat", + "continue": "Continuă", + "pause": "Repauz", + "stop": "Stop", + "return_to_base": "Parchează", + "locate": "Găsește Aspirator", + "not_available": "Aspiratorul nu este disponibil" + }, + "error": { + "missing_entity": "Este necesară specificarea entității!" + }, + "editor": { + "entity": "Entitate (Necesar)", + "map": "Camera Harta (Optional)", + "image": "Imagine (Optional)", + "compact_view": "Vizualizare compactă", + "compact_view_aria_label_on": "Pornește vizualizare compactă", + "compact_view_aria_label_off": "Oprește vizualizare compactă compact view off", + "show_name": "Arată Nume", + "show_name_aria_label_on": "Pornește arată nume", + "show_name_aria_label_off": "Oprește arată nume", + "show_status": "Arată Status", + "show_status_aria_label_on": "Pornește arată status", + "show_status_aria_label_off": "Oprește arată status", + "show_toolbar": "Arată bara de instrumente", + "show_toolbar_aria_label_on": "Pornește arată bara de instrumente", + "show_toolbar_aria_label_off": "Oprește arată bara de instrumente", + "code_only_note": "Notă: Acțiunile de setare și opțiunile de statistici sunt disponibile exclusiv folosind Editorul de cod." } - \ No newline at end of file +} diff --git a/src/translations/ru.json b/src/translations/ru.json index 27cb62da..9ccc175f 100644 --- a/src/translations/ru.json +++ b/src/translations/ru.json @@ -30,6 +30,9 @@ "error": { "missing_entity": "Объект является обязательным полем!" }, + "warning": { + "actions_array": "" + }, "editor": { "entity": "Объект (Обязательное)", "map": "Камера для карты (Опциональное)", diff --git a/src/translations/sv.json b/src/translations/sv.json index 9131b7ed..c1ace961 100644 --- a/src/translations/sv.json +++ b/src/translations/sv.json @@ -27,6 +27,9 @@ "error": { "missing_entity": "Specificera entitet är obligatoriskt!" }, + "warning": { + "actions_array": "" + }, "editor": { "entity": "Entitet (Obligatoriskt)", "map": "Kartkamera (Valfritt)", diff --git a/src/translations/uk.json b/src/translations/uk.json index 002503e2..7effe02a 100644 --- a/src/translations/uk.json +++ b/src/translations/uk.json @@ -41,6 +41,9 @@ "error": { "missing_entity": "Необхідно вказати сутність!" }, + "warning": { + "actions_array": "" + }, "editor": { "entity": "Сутність (обов'язково)", "map": "Камера для карти (Додатково)", diff --git a/src/vacuum-card.js b/src/vacuum-card.js index f8748e51..ee67e2a9 100755 --- a/src/vacuum-card.js +++ b/src/vacuum-card.js @@ -103,6 +103,12 @@ class VacuumCard extends LitElement { if (!config.entity) { throw new Error(localize('error.missing_entity')); } + + const actions = config.actions; + if (actions && Array.isArray(actions)) { + console.warn(localize('warning.actions_array')); + } + this.config = config; } @@ -161,6 +167,66 @@ class VacuumCard extends LitElement { this.callService('set_fan_speed', false, { fan_speed }); } + handleStart() { + const actions = this.config.actions; + if (!actions || !actions.start) { + this.callService('start'); + return; + } + + this.callAction(actions.start); + } + + handlePause() { + const actions = this.config.actions; + if (!actions || !actions.pause) { + this.callService('pause'); + return; + } + + this.callAction(actions.pause); + } + + handleResume() { + const actions = this.config.actions; + if (!actions || !actions.resume) { + this.callService('start'); + return; + } + + this.callAction(actions.resume); + } + + handleStop() { + const actions = this.config.actions; + if (!actions || !actions.stop) { + this.callService('stop'); + return; + } + + this.callAction(actions.stop); + } + + handleLocate() { + const actions = this.config.actions; + if (!actions || !actions.locate) { + this.callService('locate', false); + return; + } + + this.callAction(actions.locate); + } + + handleReturnToBase() { + const actions = this.config.actions; + if (!actions || !actions.return_to_base) { + this.callService('return_to_base'); + return; + } + + this.callAction(actions.return_to_base); + } + callService(service, isRequest = true, options = {}) { this.hass.callService('vacuum', service, { entity_id: this.config.entity, @@ -173,6 +239,12 @@ class VacuumCard extends LitElement { } } + callAction(action) { + const { service, service_data } = action; + const [domain, name] = service.split('.'); + this.hass.callService(domain, name, service_data); + } + getAttributes(entity) { const { status, @@ -332,15 +404,15 @@ class VacuumCard extends LitElement { case 'cleaning': { return html`
- + ${localize('common.pause')} - + ${localize('common.stop')} - + ${localize('common.return_to_base')} @@ -351,11 +423,11 @@ class VacuumCard extends LitElement { case 'paused': { return html`
- + ${localize('common.continue')} - + ${localize('common.return_to_base')} @@ -366,11 +438,11 @@ class VacuumCard extends LitElement { case 'returning': { return html`
- + ${localize('common.continue')} - + ${localize('common.pause')} @@ -380,24 +452,24 @@ class VacuumCard extends LitElement { case 'docked': case 'idle': default: { - const { actions = [] } = this.config; - - const buttons = actions.map(({ name, service, icon, service_data }) => { - const execute = () => { - const [domain, name] = service.split('.'); - this.hass.callService(domain, name, service_data); - }; - return html``; - }); + const { shortcuts = [] } = this.config; + + const buttons = shortcuts.map( + ({ name, service, icon, service_data }) => { + const execute = () => { + this.callAction({ service, service_data }); + }; + return html``; + } + ); const dockButton = html` + @click="${this.handleReturnToBase}" + > `; @@ -405,14 +477,14 @@ class VacuumCard extends LitElement {
+ @click="${this.handleStart}" + > + @click="${this.handleLocate}" + > ${state === 'idle' ? dockButton : ''} @@ -444,11 +516,7 @@ class VacuumCard extends LitElement { return html` -
+
${this.renderSource()}