From 41b86e6c10a47e5b224322ca4aa90a56466bc9ff Mon Sep 17 00:00:00 2001 From: Zack Barett Date: Wed, 9 Sep 2020 17:03:11 -0500 Subject: [PATCH 01/41] Fix media browse item width (#6870) --- src/components/media-player/ha-media-player-browse.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/components/media-player/ha-media-player-browse.ts b/src/components/media-player/ha-media-player-browse.ts index b2468fbbd3..741a85b238 100644 --- a/src/components/media-player/ha-media-player-browse.ts +++ b/src/components/media-player/ha-media-player-browse.ts @@ -630,13 +630,20 @@ export class HaMediaPlayerBrowse extends LitElement { display: grid; grid-template-columns: repeat( auto-fit, - minmax(var(--media-browse-item-size, 175px), 0.33fr) + minmax(var(--media-browse-item-size, 175px), 0.1fr) ); grid-gap: 16px; margin: 8px 0px; padding: 0px 24px; } + :host([dialog]) .children { + grid-template-columns: repeat( + auto-fit, + minmax(var(--media-browse-item-size, 175px), 0.33fr) + ); + } + .child { display: flex; flex-direction: column; @@ -689,6 +696,7 @@ export class HaMediaPlayerBrowse extends LitElement { .child .title { font-size: 16px; padding-top: 8px; + padding-left: 2px; overflow: hidden; display: -webkit-box; -webkit-box-orient: vertical; From cdf7558a8e61df7aa85910b6671dccacf9f1203e Mon Sep 17 00:00:00 2001 From: HomeAssistant Azure Date: Thu, 10 Sep 2020 00:32:03 +0000 Subject: [PATCH 02/41] [ci skip] Translation update --- translations/frontend/cs.json | 6 ++-- translations/frontend/en.json | 4 +++ translations/frontend/nb.json | 50 +++++++++++++++--------------- translations/frontend/uk.json | 9 ++++-- translations/frontend/zh-Hant.json | 25 +++++++++++++-- 5 files changed, 61 insertions(+), 33 deletions(-) diff --git a/translations/frontend/cs.json b/translations/frontend/cs.json index 2c11c2fd88..dec6bdf715 100644 --- a/translations/frontend/cs.json +++ b/translations/frontend/cs.json @@ -1497,7 +1497,7 @@ "header": "Konfigurace Home Assistant", "helpers": { "caption": "Pomocníci", - "description": "Prvky, které mohou pomoci při vytváření automatizací.", + "description": "Správa prvků, které mohou pomoci při vytváření automatizací", "dialog": { "add_helper": "Přidat pomocníka", "add_platform": "Přidat {platform}", @@ -1628,7 +1628,7 @@ "logs": { "caption": "Logy", "clear": "Zrušit", - "description": "Zobrazit log Home Assistant", + "description": "Zobrazení logů Home Assistant", "details": "Detaily protokolu ({level})", "load_full_log": "Načíst úplný protokol Home Assistanta", "loading_log": "Načítání protokolu chyb...", @@ -2179,7 +2179,7 @@ "configured_in_yaml": "Zóny konfigurované pomocí configuration.yaml nelze upravovat pomocí UI.", "confirm_delete": "Opravdu chcete tuto zónu smazat?", "create_zone": "Vytvořit zónu", - "description": "Spravujte zóny, ve kterých chcete sledovat osoby.", + "description": "Spravá zón, ve kterých chcete sledovat osoby", "detail": { "create": "Vytvořit", "delete": "Smazat", diff --git a/translations/frontend/en.json b/translations/frontend/en.json index 250666624b..180e20eb4c 100644 --- a/translations/frontend/en.json +++ b/translations/frontend/en.json @@ -567,6 +567,9 @@ "loading_history": "Loading state history...", "no_history_found": "No state history found." }, + "logbook": { + "entries_not_found": "No logbook entries found." + }, "media-browser": { "audio_not_supported": "Your browser does not support the audio element.", "choose_player": "Choose Player", @@ -703,6 +706,7 @@ }, "more_info_control": { "controls": "Controls", + "details": "Details", "dismiss": "Dismiss dialog", "edit": "Edit entity", "history": "History", diff --git a/translations/frontend/nb.json b/translations/frontend/nb.json index f650fd7020..c01cf5a5a5 100644 --- a/translations/frontend/nb.json +++ b/translations/frontend/nb.json @@ -625,12 +625,12 @@ "dialogs": { "config_entry_system_options": { "enable_new_entities_description": "Hvis den er deaktivert, blir ikke nyoppdagede entiteter for {integration} automatisk lagt til i Home Assistant.", - "enable_new_entities_label": "Aktiver enheter som nylig er lagt til.", + "enable_new_entities_label": "Aktiver entiteter som nylig er lagt til.", "title": "Systemalternativer for {integration}", "update": "Oppdater" }, "domain_toggler": { - "reset_entities": "Tilbakestill enheter", + "reset_entities": "Tilbakestill entiteter", "title": "Veksle domener" }, "entity_registry": { @@ -744,7 +744,7 @@ }, "mqtt_device_debug_info": { "deserialize": "Forsøk å analysere MQTT-meldinger som JSON", - "entities": "Enheter", + "entities": "Entiteter", "no_entities": "Ingen entiteter", "no_triggers": "Ingen utløsere", "payload_display": "Nyttelastvisning", @@ -1120,7 +1120,7 @@ "seconds": "Sekunder" }, "time": { - "at": "Klokken", + "at": "På tidspunktet", "label": "Tid", "type_input": "Verdien til en tid/dato hjelper", "type_value": "Bestemt tid" @@ -1193,7 +1193,7 @@ "manage_entities": "Håndtér entiteter", "security_devices": "Sikkerhetsenheter", "sync_entities": "Synkronisér entiteter til Google", - "sync_entities_404_message": "Kunne ikke synkronisere enhetene dine med Google, be Google 'Hei Google, synkroniser enhetene mine' for å synkronisere enhetene dine.", + "sync_entities_404_message": "Kunne ikke synkronisere entitetene dine med Google, be Google 'Hei Google, synkroniser enhetene mine' for å synkronisere entitetene dine.", "title": "" }, "integrations": "Integrasjoner", @@ -1229,15 +1229,15 @@ }, "alexa": { "banner": "Redigere hvilke entiteter som vises via dette grensesnittet er deaktivert fordi du har konfigurert entitetsfilter i configuration.yaml.", - "dont_expose_entity": "Ikke eksponer enheten", + "dont_expose_entity": "Ikke eksponer entiteten", "expose": "Eksponer til Alexa", - "expose_entity": "Vis enhet", + "expose_entity": "Eksponer entitet", "exposed": "{selected} eksponert", "exposed_entities": "Eksponerte entiteter", "follow_domain": "Følg domenet", "manage_domains": "Administrer domener", "not_exposed": "{selected} ikke eksponert", - "not_exposed_entities": "Ikke eksponerte enheter", + "not_exposed_entities": "Ikke eksponerte entiteter", "title": "" }, "caption": "", @@ -1274,15 +1274,15 @@ "google": { "banner": "Redigere hvilke entiteter som vises via dette grensesnittet er deaktivert fordi du har konfigurert entitetsfilter i configuration.yaml.", "disable_2FA": "Deaktiver totrinnsbekreftelse", - "dont_expose_entity": "Ikke eksponer enheten", + "dont_expose_entity": "Ikke eksponer entiteten", "expose": "Eksponer til Google Assistant", - "expose_entity": "Vis enhet", + "expose_entity": "Eksponer entiteten", "exposed": "{selected} eksponert", "exposed_entities": "Eksponerte entiteter", "follow_domain": "Følg domenet", "manage_domains": "Administrer domener", "not_exposed": "{selected} ikke eksponert", - "not_exposed_entities": "Ikke eksponerte enheter", + "not_exposed_entities": "Ikke eksponerte entiteter", "sync_to_google": "Synkroniserer endringer til Google.", "title": "" }, @@ -1383,7 +1383,7 @@ } }, "devices": { - "add_prompt": "Ingen {navn} er lagt til ved hjelp av denne enheten ennå. Du kan legge til en ved å klikke på + knappen ovenfor.", + "add_prompt": "Ingen {name} er lagt til ved hjelp av denne enheten ennå. Du kan legge til en ved å klikke på + knappen ovenfor.", "automation": { "actions": { "caption": "Når noe er utløst..." @@ -1403,7 +1403,7 @@ "caption": "Enheter", "confirm_delete": "Er du sikker på at du vil slette denne enheten?", "confirm_rename_entity_ids": "Vil du også endre navn på entitets-ID-en for entitetene dine?", - "confirm_rename_entity_ids_warning": "Dette vil ikke endre noen konfigurasjon (som automatiseringer, skript, scener, Lovelace) som bruker disse enhetene, må du oppdatere dem selv.", + "confirm_rename_entity_ids_warning": "Dette vil ikke endre noen konfigurasjon (som automatiseringer, skript, scener, Lovelace) som bruker disse entitetene, du må oppdatere dem selv.", "data_table": { "area": "Område", "battery": "Batteri", @@ -1421,9 +1421,9 @@ "entities": { "add_entities_lovelace": "Legg til i Lovelace", "disabled_entities": "+{count} {count, plural,\n one {deaktivert entitet}\n other {deaktiverte entiteter}\n}", - "entities": "Enheter", + "entities": "Entiteter", "hide_disabled": "Skjul deaktiverte", - "none": "Denne enheten har ingen enheter" + "none": "Denne enheten har ingen entiteter" }, "name": "Navn", "no_devices": "Ingen enheter", @@ -1461,7 +1461,7 @@ "filter": "", "show_disabled": "Vis deaktiverte entiteter", "show_readonly": "Vis skrivebeskyttede enheter", - "show_unavailable": "Vis utilgjengelige enheter" + "show_unavailable": "Vis utilgjengelige entiteter" }, "header": "Entiteter", "headers": { @@ -1870,7 +1870,7 @@ "delete": "Slett entitet", "device_entities": "Hvis du legger til en entitet som tilhører en enhet, vil enheten også bli lagt til.", "header": "Entiteter", - "introduction": "Enheter som ikke tilhører en enhet kan angis her.", + "introduction": "Entiteter som ikke tilhører en enhet kan angis her.", "without_device": "Entiteter uten enhet" }, "icon": "Ikon", @@ -1967,19 +1967,19 @@ "input_text": "Last inn inndata tekst på nytt", "introduction": "Noen deler av Home Assistant kan laste inn uten å kreve omstart. Hvis du trykker last på nytt, vil du bytte den nåværende konfigurasjonen med den nye.", "min_max": "Last inn min/maks entiteter på nytt", - "mqtt": "Last inn mqtt-enheter på nytt", + "mqtt": "Last inn mqtt-entiteter på nytt", "person": "Last inn personer på nytt", "ping": "Last inn ping binære sensor entiteter på nytt", "reload": "Last inn {domain} på nytt", - "rest": "Last inn hvileenheter på nytt og varsle tjenester", - "rpi_gpio": "Last inn Raspberry Pi GPIO-enheter på nytt", + "rest": "Last inn REST entiteter og varslingstjenester på nytt", + "rpi_gpio": "Last inn Raspberry Pi GPIO-entiteter på nytt", "scene": "Last inn scener på nytt", "script": "Last inn skript på nytt", "smtp": "Last inn smtp-varslingstjenester på nytt", "statistics": "Last inn statistiske entiteter på nytt", "telegram": "Last inn telegram varslingstjenester på nytt", "template": "Laste inn mal entiteter på nytt", - "trend": "Laste inn trend entiteter på nytt", + "trend": "Last inn trend entiteter på nytt", "universal": "Laste inn universelle mediespiller entiteter på nytt", "zone": "Last inn soner på nytt" }, @@ -2599,7 +2599,7 @@ }, "cardpicker": { "by_card": "Med kort", - "by_entity": "Etter enhet", + "by_entity": "Etter entitet", "custom_card": "Tilpasset", "domain": "Domene", "entity": "Entitet", @@ -2716,14 +2716,14 @@ "refresh_header": "Vil du oppfriske?" }, "unused_entities": { - "available_entities": "Dette er enheter som du har tilgjengelig, men som ikke er i Lovelace brukergrensesnittet ennå.", + "available_entities": "Dette er entitene som du har tilgjengelig, men som ikke er i Lovelace brukergrensesnittet ennå.", "domain": "Domene", "entity": "Entitet", "entity_id": "Entitets-ID", "last_changed": "Sist endret", "no_data": "Ingen ubrukte entiteter funnet", - "search": "Søk etter enheter", - "select_to_add": "Velg enhetene du vil legge til et kort, og klikk deretter på knappen Legg til kort.", + "search": "Søk etter entiteter", + "select_to_add": "Velg entitene du vil legge til et kort, og klikk deretter på knappen Legg til kort.", "title": "Ubrukte entiteter" }, "views": { diff --git a/translations/frontend/uk.json b/translations/frontend/uk.json index 4757374de4..b3501dd8da 100644 --- a/translations/frontend/uk.json +++ b/translations/frontend/uk.json @@ -810,6 +810,9 @@ "label": "Викликати сервіс", "service_data": "Дані сервісу" }, + "wait_for_trigger": { + "timeout": "Таймаут (опціонально)" + }, "wait_template": { "label": "Чекати", "timeout": "Тайм-аут (необов'язково)", @@ -873,7 +876,8 @@ "time": { "after": "Після", "before": "До", - "label": "Час" + "label": "Час", + "type_value": "Фіксований час" }, "zone": { "entity": "Об'єкт з місцем розташування", @@ -982,7 +986,8 @@ }, "time": { "at": "У", - "label": "Час" + "label": "Час", + "type_value": "Фіксований час" }, "webhook": { "label": "Webhook", diff --git a/translations/frontend/zh-Hant.json b/translations/frontend/zh-Hant.json index 30f780c639..67203a2c85 100644 --- a/translations/frontend/zh-Hant.json +++ b/translations/frontend/zh-Hant.json @@ -553,6 +553,10 @@ "toggle": "觸發" }, "entity": { + "entity-attribute-picker": { + "attribute": "屬性", + "show_attributes": "顯示屬性" + }, "entity-picker": { "clear": "清除", "entity": "實體", @@ -929,7 +933,13 @@ "label": "執行服務", "service_data": "服務資料" }, + "wait_for_trigger": { + "continue_timeout": "繼續逾時計算", + "label": "等候觸發", + "timeout": "超時(選項)" + }, "wait_template": { + "continue_timeout": "繼續逾時計算", "label": "等待", "timeout": "超時(選項)", "wait_template": "等待模板" @@ -993,7 +1003,9 @@ "time": { "after": "在...之後", "before": "在...之前", - "label": "時間" + "label": "時間", + "type_input": "日期/時間協助數值", + "type_value": "固定時間" }, "zone": { "entity": "區域實體", @@ -1081,6 +1093,7 @@ "value_template": "數值模板(選項)" }, "state": { + "attribute": "屬性(選項)", "for": "持續", "from": "從...狀態", "label": "狀態", @@ -1107,8 +1120,10 @@ "seconds": "秒鐘" }, "time": { - "at": "在...時間", - "label": "時間" + "at": "時間於", + "label": "時間", + "type_input": "日期/時間協助數值", + "type_value": "固定時間" }, "webhook": { "label": "Webhook", @@ -1131,6 +1146,8 @@ "add_automation": "新增一個自動化", "delete_automation": "刪除自動化", "delete_confirm": "確定要刪除此自動化?", + "duplicate": "複製", + "duplicate_automation": "複製自動化", "edit_automation": "編輯自動化", "header": "自動化編輯器", "headers": { @@ -1531,6 +1548,7 @@ }, "integrations": { "add_integration": "新增整合", + "attention": "需要注意", "caption": "整合", "config_entry": { "area": "於 {area}", @@ -1601,6 +1619,7 @@ "none_found_detail": "調整搜尋條件。", "note_about_integrations": "目前並非所有整合皆可以透過 UI 進行設定。", "note_about_website_reference": "更多資訊請參閱", + "reconfigure": "重新設定", "rename_dialog": "編輯設定實體名稱", "rename_input_label": "實體名稱", "search": "搜尋整合" From af74f21af9867f65789d6b1b20d47924eb464e20 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Thu, 10 Sep 2020 11:12:05 +0200 Subject: [PATCH 03/41] Dont virtualize logbook in more info (#6907) --- src/dialogs/more-info/ha-more-info-history.ts | 10 +- src/panels/logbook/ha-logbook.ts | 183 +++++++++--------- src/panels/logbook/ha-panel-logbook.ts | 37 ++-- 3 files changed, 116 insertions(+), 114 deletions(-) diff --git a/src/dialogs/more-info/ha-more-info-history.ts b/src/dialogs/more-info/ha-more-info-history.ts index 4c57bbd2c3..50390b31a2 100644 --- a/src/dialogs/more-info/ha-more-info-history.ts +++ b/src/dialogs/more-info/ha-more-info-history.ts @@ -8,7 +8,6 @@ import { PropertyValues, TemplateResult, } from "lit-element"; -import { styleMap } from "lit-html/directives/style-map"; import { computeStateDomain } from "../../common/entity/compute_state_domain"; import "../../components/ha-circular-progress"; import "../../components/state-history-charts"; @@ -16,7 +15,7 @@ import { getRecentWithCache } from "../../data/cached-history"; import { HistoryResult } from "../../data/history"; import { getLogbookData, LogbookEntry } from "../../data/logbook"; import "../../panels/logbook/ha-logbook"; -import { haStyle } from "../../resources/styles"; +import { haStyle, haStyleScrollbar } from "../../resources/styles"; import { HomeAssistant } from "../../types"; @customElement("ha-more-info-history") @@ -59,12 +58,10 @@ export class MoreInfoHistory extends LitElement { : this._entries.length ? html` - ${scroll({ - items: this.entries, - renderItem: (item: LogbookEntry, index?: number) => - this._renderLogbookItem(item, index), - })} + ${this.virtualize + ? scroll({ + items: this.entries, + renderItem: (item: LogbookEntry, index?: number) => + this._renderLogbookItem(item, index), + }) + : this.entries.map((item, index) => + this._renderLogbookItem(item, index) + )} `; } @@ -185,106 +191,103 @@ class HaLogbook extends LitElement { }); } - static get styles(): CSSResult[] { - return [ - haStyleScrollbar, - css` - :host { - display: block; - height: 100%; - } + static get styles(): CSSResult { + return css` + :host { + display: block; + height: 100%; + } - .rtl { - direction: ltr; - } + .rtl { + direction: ltr; + } - .entry-container { - width: 100%; - } + .entry-container { + width: 100%; + } - .entry { - display: flex; - width: 100%; - line-height: 2em; - padding: 8px 16px; - box-sizing: border-box; - border-top: 1px solid - var(--mdc-dialog-scroll-divider-color, rgba(0, 0, 0, 0.12)); - } + .entry { + display: flex; + width: 100%; + line-height: 2em; + padding: 8px 16px; + box-sizing: border-box; + border-top: 1px solid + var(--mdc-dialog-scroll-divider-color, rgba(0, 0, 0, 0.12)); + } - .time { - display: flex; - justify-content: center; - flex-direction: column; - width: 75px; - flex-shrink: 0; - font-size: 12px; - color: var(--secondary-text-color); - } + .time { + display: flex; + justify-content: center; + flex-direction: column; + width: 75px; + flex-shrink: 0; + font-size: 12px; + color: var(--secondary-text-color); + } - .date { - margin: 8px 0; - padding: 0 16px; - } + .date { + margin: 8px 0; + padding: 0 16px; + } - .narrow .date { - padding: 0 8px; - } + .narrow .date { + padding: 0 8px; + } - .rtl .date { - direction: rtl; - } + .rtl .date { + direction: rtl; + } - .icon-message { - display: flex; - align-items: center; - } + .icon-message { + display: flex; + align-items: center; + } - .no-entries { - text-align: center; - } + .no-entries { + text-align: center; + } - ha-icon { - margin: 0 8px 0 16px; - flex-shrink: 0; - color: var(--primary-text-color); - } + ha-icon { + margin: 0 8px 0 16px; + flex-shrink: 0; + color: var(--primary-text-color); + } - .message { - color: var(--primary-text-color); - } + .message { + color: var(--primary-text-color); + } - .no-name .item-message { - text-transform: capitalize; - } + .no-name .item-message { + text-transform: capitalize; + } - a { - color: var(--primary-color); - } + a { + color: var(--primary-color); + } - .uni-virtualizer-host { - display: block; - position: relative; - contain: strict; - height: 100%; - overflow: auto; - } + .uni-virtualizer-host { + display: block; + position: relative; + contain: strict; + height: 100%; + overflow: auto; + } - .uni-virtualizer-host > * { - box-sizing: border-box; - } + .uni-virtualizer-host > * { + box-sizing: border-box; + } - .narrow .entry { - flex-direction: column; - line-height: 1.5; - padding: 8px; - } + .narrow .entry { + flex-direction: column; + line-height: 1.5; + padding: 8px; + } - .narrow .icon-message ha-icon { - margin-left: 0; - } - `, - ]; + .narrow .icon-message ha-icon { + margin-left: 0; + } + `; } } diff --git a/src/panels/logbook/ha-panel-logbook.ts b/src/panels/logbook/ha-panel-logbook.ts index af57dbc7e6..806cb5ac6b 100644 --- a/src/panels/logbook/ha-panel-logbook.ts +++ b/src/panels/logbook/ha-panel-logbook.ts @@ -1,33 +1,33 @@ +import { mdiRefresh } from "@mdi/js"; import "@polymer/app-layout/app-header/app-header"; import "@polymer/app-layout/app-toolbar/app-toolbar"; -import "../../components/ha-icon-button"; -import "../../components/ha-circular-progress"; -import { computeRTL } from "../../common/util/compute_rtl"; -import "../../components/entity/ha-entity-picker"; -import "../../components/ha-menu-button"; -import "../../layouts/ha-app-layout"; -import "./ha-logbook"; import { - LitElement, - property, - internalProperty, + css, customElement, html, - css, + internalProperty, + LitElement, + property, PropertyValues, } from "lit-element"; -import { HomeAssistant } from "../../types"; -import { haStyle } from "../../resources/styles"; -import { fetchUsers } from "../../data/user"; -import { fetchPersons } from "../../data/person"; +import { computeRTL } from "../../common/util/compute_rtl"; +import "../../components/entity/ha-entity-picker"; +import "../../components/ha-circular-progress"; +import "../../components/ha-date-range-picker"; +import type { DateRangePickerRanges } from "../../components/ha-date-range-picker"; +import "../../components/ha-icon-button"; +import "../../components/ha-menu-button"; import { clearLogbookCache, getLogbookData, LogbookEntry, } from "../../data/logbook"; -import { mdiRefresh } from "@mdi/js"; -import "../../components/ha-date-range-picker"; -import type { DateRangePickerRanges } from "../../components/ha-date-range-picker"; +import { fetchPersons } from "../../data/person"; +import { fetchUsers } from "../../data/user"; +import "../../layouts/ha-app-layout"; +import { haStyle } from "../../resources/styles"; +import { HomeAssistant } from "../../types"; +import "./ha-logbook"; @customElement("ha-panel-logbook") export class HaPanelLogbook extends LitElement { @@ -125,6 +125,7 @@ export class HaPanelLogbook extends LitElement { .hass=${this.hass} .entries=${this._entries} .userIdToName=${this._userIdToName} + virtualize >`} `; From 810d2a1ceb97605cebc33a6d594b40b26bd73645 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Thu, 10 Sep 2020 13:11:27 +0200 Subject: [PATCH 04/41] Fix onboarding dark mode (#6910) Fixes #6882 --- src/components/map/ha-location-editor.ts | 1 + src/html/onboarding.html.template | 7 ++++++- src/onboarding/onboarding-create-user.ts | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/components/map/ha-location-editor.ts b/src/components/map/ha-location-editor.ts index 9bfcf5e50e..3b42fd38cc 100644 --- a/src/components/map/ha-location-editor.ts +++ b/src/components/map/ha-location-editor.ts @@ -279,6 +279,7 @@ class LocationEditor extends LitElement { } #map { height: 100%; + background: inherit; } .leaflet-edit-move { border-radius: 50%; diff --git a/src/html/onboarding.html.template b/src/html/onboarding.html.template index 0a605baba1..f4bc333ede 100644 --- a/src/html/onboarding.html.template +++ b/src/html/onboarding.html.template @@ -11,7 +11,12 @@ @media (prefers-color-scheme: dark) { html { background-color: #111111; - color: var(--primary-text-color, #e1e1e1); + color: #e1e1e1; + } + ha-onboarding { + --primary-text-color: #e1e1e1; + --secondary-text-color: #9b9b9b; + --disabled-text-color: #6f6f6f; } } .content { diff --git a/src/onboarding/onboarding-create-user.ts b/src/onboarding/onboarding-create-user.ts index d12fcd97ce..bb3f27ba2e 100644 --- a/src/onboarding/onboarding-create-user.ts +++ b/src/onboarding/onboarding-create-user.ts @@ -6,9 +6,9 @@ import { CSSResult, customElement, html, + internalProperty, LitElement, property, - internalProperty, PropertyValues, TemplateResult, } from "lit-element"; From c2e6d403828095c0942f86bc3e81cd3bbfcdd663 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 10 Sep 2020 15:16:03 +0200 Subject: [PATCH 05/41] Bump http-proxy from 1.17.0 to 1.18.1 (#6914) Bumps [http-proxy](https://github.com/http-party/node-http-proxy) from 1.17.0 to 1.18.1. - [Release notes](https://github.com/http-party/node-http-proxy/releases) - [Changelog](https://github.com/http-party/node-http-proxy/blob/master/CHANGELOG.md) - [Commits](https://github.com/http-party/node-http-proxy/compare/1.17.0...1.18.1) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- yarn.lock | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/yarn.lock b/yarn.lock index 4a0450044e..1a6af52a72 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4942,7 +4942,7 @@ debug@2.6.9, debug@^2.1.2, debug@^2.2.0, debug@^2.3.3, debug@^2.6.9: dependencies: ms "2.0.0" -debug@3.2.6, debug@^3.1.0, debug@^3.1.1, debug@^3.2.5, debug@^3.2.6: +debug@3.2.6, debug@^3.1.0, debug@^3.1.1, debug@^3.2.5: version "3.2.6" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== @@ -5711,11 +5711,16 @@ event-target-shim@^5.0.1: resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== -eventemitter3@3.1.0, eventemitter3@^3.0.0: +eventemitter3@3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-3.1.0.tgz#090b4d6cdbd645ed10bf750d4b5407942d7ba163" integrity sha512-ivIvhpq/Y0uSjcHDcOIccjmYjGLcP09MFGE7ysAwkAvkXfpZlC985pH2/ui64DKazbTW/4kN3yqozUxlXzI6cA== +eventemitter3@^4.0.0: + version "4.0.7" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" + integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== + events@^3.0.0: version "3.2.0" resolved "https://registry.yarnpkg.com/events/-/events-3.2.0.tgz#93b87c18f8efcd4202a461aec4dfc0556b639379" @@ -6223,11 +6228,9 @@ fn-name@~2.0.1: integrity sha1-UhTXU3pNBqSjAcDMJi/rhBiAAuc= follow-redirects@^1.0.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.7.0.tgz#489ebc198dc0e7f64167bd23b03c4c19b5784c76" - integrity sha512-m/pZQy4Gj287eNy94nivy5wchN3Kp+Q5WgUPNy5lJSZ3sgkVKSYV/ZChMAQVIgx1SqfZ2zBZtPA2YlXIWxxJOQ== - dependencies: - debug "^3.2.6" + version "1.13.0" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.0.tgz#b42e8d93a2a7eea5ed88633676d6597bc8e384db" + integrity sha512-aq6gF1BEKje4a9i9+5jimNFIpq4Q1WiwBToeRK5NvZBd/TRsmW8BsJfOEGkr76TbOyPVD3OVDN910EcUNtRYEA== for-in@^1.0.1, for-in@^1.0.2: version "1.0.2" @@ -6977,11 +6980,11 @@ http-proxy-middleware@0.19.1: micromatch "^3.1.10" http-proxy@^1.17.0: - version "1.17.0" - resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.17.0.tgz#7ad38494658f84605e2f6db4436df410f4e5be9a" - integrity sha512-Taqn+3nNvYRfJ3bGvKfBSRwy1v6eePlm3oc/aWVxZp57DQr5Eq3xhKJi7Z4hZpS8PC3H4qI+Yly5EmFacGuA/g== + version "1.18.1" + resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549" + integrity sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ== dependencies: - eventemitter3 "^3.0.0" + eventemitter3 "^4.0.0" follow-redirects "^1.0.0" requires-port "^1.0.0" From 96c5fdcbeb517f29243488a016af5c56262c887c Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Thu, 10 Sep 2020 15:17:32 +0200 Subject: [PATCH 06/41] Fix some lovelace editors (#6911) * Fix some lovelace editors * let -> const --- .../lovelace/components/hui-action-editor.ts | 35 ++++----- .../config-elements/hui-button-card-editor.ts | 77 ++++++++++--------- .../config-elements/hui-light-card-editor.ts | 28 +++---- .../hui-picture-card-editor.ts | 26 +++---- .../hui-picture-entity-card-editor.ts | 76 +++++++++--------- .../hui-picture-glance-card-editor.ts | 49 ++++-------- 6 files changed, 131 insertions(+), 160 deletions(-) diff --git a/src/panels/lovelace/components/hui-action-editor.ts b/src/panels/lovelace/components/hui-action-editor.ts index 5899360217..dea53bd0d3 100644 --- a/src/panels/lovelace/components/hui-action-editor.ts +++ b/src/panels/lovelace/components/hui-action-editor.ts @@ -1,4 +1,5 @@ import "@polymer/paper-dropdown-menu/paper-dropdown-menu"; +import "@polymer/paper-input/paper-input"; import "@polymer/paper-input/paper-textarea"; import "@polymer/paper-item/paper-item"; import "@polymer/paper-listbox/paper-listbox"; @@ -9,7 +10,7 @@ import { property, TemplateResult, } from "lit-element"; -import { fireEvent, HASSDomEvent } from "../../../common/dom/fire_event"; +import { fireEvent } from "../../../common/dom/fire_event"; import "../../../components/ha-service-picker"; import { ActionConfig, @@ -20,17 +21,6 @@ import { import { HomeAssistant } from "../../../types"; import { EditorTarget } from "../editor/types"; -declare global { - // for fire event - interface HASSDomEvents { - "action-changed": undefined; - } - // for add event listener - interface HTMLElementEventMap { - "action-changed": HASSDomEvent; - } -} - @customElement("hui-action-editor") export class HuiActionEditor extends LitElement { @property() public config?: ActionConfig; @@ -42,21 +32,21 @@ export class HuiActionEditor extends LitElement { @property() protected hass?: HomeAssistant; get _action(): string { - return this.config!.action || ""; + return this.config?.action || ""; } get _navigation_path(): string { - const config = this.config! as NavigateActionConfig; + const config = this.config as NavigateActionConfig; return config.navigation_path || ""; } get _url_path(): string { - const config = this.config! as UrlActionConfig; + const config = this.config as UrlActionConfig; return config.url_path || ""; } get _service(): string { - const config = this.config! as CallServiceActionConfig; + const config = this.config as CallServiceActionConfig; return config.service || ""; } @@ -107,13 +97,14 @@ export class HuiActionEditor extends LitElement { .configValue="${"service"}" @value-changed="${this._valueChanged}" > -

Toggle Editor to input Service Data

+ Service data can only be entered in the code editor ` : ""} `; } private _valueChanged(ev: Event): void { + ev.stopPropagation(); if (!this.hass) { return; } @@ -121,12 +112,12 @@ export class HuiActionEditor extends LitElement { if (this[`_${target.configValue}`] === target.value) { return; } - if (target.configValue === "action") { - this.config = { action: "none" }; - } if (target.configValue) { - this.config = { ...this.config!, [target.configValue!]: target.value }; - fireEvent(this, "action-changed"); + const newConfig = + target.configValue === "action" + ? { action: target.value } + : { ...this.config!, [target.configValue!]: target.value }; + fireEvent(this, "value-changed", { value: newConfig }); } } } diff --git a/src/panels/lovelace/editor/config-elements/hui-button-card-editor.ts b/src/panels/lovelace/editor/config-elements/hui-button-card-editor.ts index 36ce2aab96..a2285c087d 100644 --- a/src/panels/lovelace/editor/config-elements/hui-button-card-editor.ts +++ b/src/panels/lovelace/editor/config-elements/hui-button-card-editor.ts @@ -2,14 +2,18 @@ import "@polymer/paper-input/paper-input"; import { customElement, html, + internalProperty, LitElement, property, - internalProperty, TemplateResult, } from "lit-element"; +import { assert, boolean, object, optional, string } from "superstruct"; import { fireEvent } from "../../../../common/dom/fire_event"; import { stateIcon } from "../../../../common/entity/state_icon"; +import { computeRTLDirection } from "../../../../common/util/compute_rtl"; +import "../../../../components/ha-formfield"; import "../../../../components/ha-icon-input"; +import "../../../../components/ha-switch"; import { ActionConfig } from "../../../../data/lovelace"; import { HomeAssistant } from "../../../../types"; import { ButtonCardConfig } from "../../cards/types"; @@ -17,16 +21,8 @@ import "../../components/hui-action-editor"; import "../../components/hui-entity-editor"; import "../../components/hui-theme-select-editor"; import { LovelaceCardEditor } from "../../types"; -import { - actionConfigStruct, - EditorTarget, - EntitiesEditorEvent, -} from "../types"; -import "../../../../components/ha-switch"; -import "../../../../components/ha-formfield"; +import { actionConfigStruct, EditorTarget } from "../types"; import { configElementStyle } from "./config-elements-style"; -import { computeRTLDirection } from "../../../../common/util/compute_rtl"; -import { assert, object, string, optional, boolean } from "superstruct"; const cardConfigStruct = object({ type: string(), @@ -63,11 +59,11 @@ export class HuiButtonCardEditor extends LitElement } get _show_name(): boolean { - return this._config!.show_name || true; + return this._config!.show_name ?? true; } get _show_state(): boolean { - return this._config!.show_state || false; + return this._config!.show_state ?? false; } get _icon(): string { @@ -75,7 +71,7 @@ export class HuiButtonCardEditor extends LitElement } get _show_icon(): boolean { - return this._config!.show_icon || true; + return this._config!.show_icon ?? true; } get _icon_height(): string { @@ -85,11 +81,11 @@ export class HuiButtonCardEditor extends LitElement } get _tap_action(): ActionConfig { - return this._config!.tap_action || { action: "more-info" }; + return this._config!.tap_action || { action: "toggle" }; } get _hold_action(): ActionConfig { - return this._config!.hold_action || { action: "none" }; + return this._config!.hold_action || { action: "more-info" }; } get _theme(): string { @@ -123,7 +119,7 @@ export class HuiButtonCardEditor extends LitElement .hass=${this.hass} .value="${this._entity}" .configValue=${"entity"} - @change="${this._valueChanged}" + @value-changed="${this._valueChanged}" allow-custom-entity >
@@ -161,7 +157,7 @@ export class HuiButtonCardEditor extends LitElement
@@ -175,7 +171,7 @@ export class HuiButtonCardEditor extends LitElement @@ -189,7 +185,7 @@ export class HuiButtonCardEditor extends LitElement @@ -225,7 +221,7 @@ export class HuiButtonCardEditor extends LitElement .config="${this._tap_action}" .actions="${actions}" .configValue="${"tap_action"}" - @action-changed="${this._valueChanged}" + @value-changed="${this._valueChanged}" > `; } - private _valueChanged(ev: EntitiesEditorEvent): void { + private _change(ev: Event) { if (!this._config || !this.hass) { return; } const target = ev.target! as EditorTarget; + const value = target.checked; - if ( - this[`_${target.configValue}`] === target.value || - this[`_${target.configValue}`] === target.config - ) { + if (this[`_${target.configValue}`] === value) { + return; + } + + this._config = { + ...this._config, + [target.configValue!]: value, + }; + fireEvent(this, "config-changed", { config: this._config }); + } + + private _valueChanged(ev: CustomEvent): void { + if (!this._config || !this.hass) { + return; + } + const target = ev.target! as EditorTarget; + const value = ev.detail.value; + + if (this[`_${target.configValue}`] === value) { return; } if (target.configValue) { - if (target.value === "") { + if (value !== false && !value) { this._config = { ...this._config }; delete this._config[target.configValue!]; } else { @@ -266,18 +278,11 @@ export class HuiButtonCardEditor extends LitElement target.configValue === "icon_height" && !isNaN(Number(target.value)) ) { - newValue = `${String(target.value)}px`; + newValue = `${String(value)}px`; } this._config = { ...this._config, - [target.configValue!]: - target.checked !== undefined - ? target.checked - : newValue !== undefined - ? newValue - : target.value - ? target.value - : target.config, + [target.configValue!]: newValue !== undefined ? newValue : value, }; } } diff --git a/src/panels/lovelace/editor/config-elements/hui-light-card-editor.ts b/src/panels/lovelace/editor/config-elements/hui-light-card-editor.ts index 6e37bd0358..90b63d4276 100644 --- a/src/panels/lovelace/editor/config-elements/hui-light-card-editor.ts +++ b/src/panels/lovelace/editor/config-elements/hui-light-card-editor.ts @@ -2,11 +2,12 @@ import "@polymer/paper-input/paper-input"; import { customElement, html, + internalProperty, LitElement, property, - internalProperty, TemplateResult, } from "lit-element"; +import { assert, object, optional, string } from "superstruct"; import { fireEvent } from "../../../../common/dom/fire_event"; import { stateIcon } from "../../../../common/entity/state_icon"; import "../../../../components/ha-icon-input"; @@ -17,13 +18,8 @@ import "../../components/hui-action-editor"; import "../../components/hui-entity-editor"; import "../../components/hui-theme-select-editor"; import { LovelaceCardEditor } from "../../types"; -import { - actionConfigStruct, - EditorTarget, - EntitiesEditorEvent, -} from "../types"; +import { actionConfigStruct, EditorTarget } from "../types"; import { configElementStyle } from "./config-elements-style"; -import { string, object, optional, assert } from "superstruct"; const cardConfigStruct = object({ type: string(), @@ -100,7 +96,7 @@ export class HuiLightCardEditor extends LitElement .value=${this._entity} .configValue=${"entity"} .includeDomains=${includeDomains} - @change=${this._valueChanged} + @value-changed=${this._valueChanged} allow-custom-entity >
@@ -145,7 +141,7 @@ export class HuiLightCardEditor extends LitElement .config=${this._hold_action} .actions=${actions} .configValue=${"hold_action"} - @action-changed=${this._valueChanged} + @value-changed=${this._valueChanged} >
`; } - private _valueChanged(ev: EntitiesEditorEvent): void { + private _valueChanged(ev: CustomEvent): void { if (!this._config || !this.hass) { return; } const target = ev.target! as EditorTarget; + const value = ev.detail.value; - if ( - this[`_${target.configValue}`] === target.value || - this[`_${target.configValue}`] === target.config - ) { + if (this[`_${target.configValue}`] === value) { return; } if (target.configValue) { - if (target.value === "") { + if (value !== false && !value) { this._config = { ...this._config }; delete this._config[target.configValue!]; } else { this._config = { ...this._config, - [target.configValue!]: target.value ? target.value : target.config, + [target.configValue!]: value, }; } } diff --git a/src/panels/lovelace/editor/config-elements/hui-picture-card-editor.ts b/src/panels/lovelace/editor/config-elements/hui-picture-card-editor.ts index 98fbf6af9a..8858545325 100644 --- a/src/panels/lovelace/editor/config-elements/hui-picture-card-editor.ts +++ b/src/panels/lovelace/editor/config-elements/hui-picture-card-editor.ts @@ -2,11 +2,12 @@ import "@polymer/paper-input/paper-input"; import { customElement, html, + internalProperty, LitElement, property, - internalProperty, TemplateResult, } from "lit-element"; +import { assert, object, optional, string } from "superstruct"; import { fireEvent } from "../../../../common/dom/fire_event"; import { ActionConfig } from "../../../../data/lovelace"; import { HomeAssistant } from "../../../../types"; @@ -14,13 +15,8 @@ import { PictureCardConfig } from "../../cards/types"; import "../../components/hui-action-editor"; import "../../components/hui-theme-select-editor"; import { LovelaceCardEditor } from "../../types"; -import { - actionConfigStruct, - EditorTarget, - EntitiesEditorEvent, -} from "../types"; +import { actionConfigStruct, EditorTarget } from "../types"; import { configElementStyle } from "./config-elements-style"; -import { string, object, optional, assert } from "superstruct"; const cardConfigStruct = object({ type: string(), @@ -89,7 +85,7 @@ export class HuiPictureCardEditor extends LitElement .config="${this._tap_action}" .actions="${actions}" .configValue="${"tap_action"}" - @action-changed="${this._valueChanged}" + @value-changed="${this._valueChanged}" > @@ -184,8 +180,7 @@ export class HuiPictureEntityCardEditor extends LitElement )} (${this.hass.localize( "ui.panel.lovelace.editor.card.config.optional" )})" - type="number" - .value="${Number(this._aspect_ratio.replace("%", ""))}" + .value="${this._aspect_ratio}" .configValue="${"aspect_ratio"}" @value-changed="${this._valueChanged}" > @@ -201,7 +196,7 @@ export class HuiPictureEntityCardEditor extends LitElement @@ -215,7 +210,7 @@ export class HuiPictureEntityCardEditor extends LitElement @@ -231,7 +226,7 @@ export class HuiPictureEntityCardEditor extends LitElement .config="${this._tap_action}" .actions="${actions}" .configValue="${"tap_action"}" - @action-changed="${this._valueChanged}" + @value-changed="${this._valueChanged}" > @@ -180,8 +171,7 @@ export class HuiPictureGlanceCardEditor extends LitElement )} (${this.hass.localize( "ui.panel.lovelace.editor.card.config.optional" )})" - type="number" - .value="${Number(this._aspect_ratio.replace("%", ""))}" + .value="${this._aspect_ratio}" .configValue="${"aspect_ratio"}" @value-changed="${this._valueChanged}" > @@ -195,7 +185,7 @@ export class HuiPictureGlanceCardEditor extends LitElement .hass=${this.hass} .value="${this._entity}" .configValue=${"entity"} - @change="${this._valueChanged}" + @value-changed="${this._valueChanged}" allow-custom-entity >
@@ -209,7 +199,7 @@ export class HuiPictureGlanceCardEditor extends LitElement .config="${this._tap_action}" .actions="${actions}" .configValue="${"tap_action"}" - @action-changed="${this._valueChanged}" + @value-changed="${this._valueChanged}" >
Date: Thu, 10 Sep 2020 16:59:12 +0200 Subject: [PATCH 07/41] Diable tts inputs when entities is unavailable (#6909) Fixes #6890 --- src/dialogs/more-info/controls/more-info-media_player.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/dialogs/more-info/controls/more-info-media_player.ts b/src/dialogs/more-info/controls/more-info-media_player.ts index 45dee3e224..2a54b7053c 100644 --- a/src/dialogs/more-info/controls/more-info-media_player.ts +++ b/src/dialogs/more-info/controls/more-info-media_player.ts @@ -188,14 +188,17 @@ class MoreInfoMediaPlayer extends LitElement {
- +
` From 7fa9f10c30a47f24dfa713754b915c5cba3838ef Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Thu, 10 Sep 2020 17:24:01 +0200 Subject: [PATCH 08/41] Don't add space on the bottom when not showing tabs (#6913) --- src/layouts/hass-tabs-subpage.ts | 36 +++++++++++++++++++------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/src/layouts/hass-tabs-subpage.ts b/src/layouts/hass-tabs-subpage.ts index 2f35492c5c..c110a02836 100644 --- a/src/layouts/hass-tabs-subpage.ts +++ b/src/layouts/hass-tabs-subpage.ts @@ -3,26 +3,26 @@ import { css, CSSResult, customElement, + eventOptions, html, + internalProperty, LitElement, property, - internalProperty, PropertyValues, TemplateResult, - eventOptions, } from "lit-element"; import { classMap } from "lit-html/directives/class-map"; import memoizeOne from "memoize-one"; import { isComponentLoaded } from "../common/config/is_component_loaded"; -import { navigate } from "../common/navigate"; -import "../components/ha-menu-button"; -import "../components/ha-icon-button-arrow-prev"; -import { HomeAssistant, Route } from "../types"; -import "../components/ha-svg-icon"; -import "../components/ha-icon"; -import "../components/ha-tab"; import { restoreScroll } from "../common/decorators/restore-scroll"; +import { navigate } from "../common/navigate"; import { computeRTL } from "../common/util/compute_rtl"; +import "../components/ha-icon"; +import "../components/ha-icon-button-arrow-prev"; +import "../components/ha-menu-button"; +import "../components/ha-svg-icon"; +import "../components/ha-tab"; +import { HomeAssistant, Route } from "../types"; export interface PageNavigation { path: string; @@ -132,7 +132,7 @@ class HassTabsSubpage extends LitElement { this.hass.language, this.narrow ); - + const showTabs = tabs.length > 1 || !this.narrow; return html`
${this.mainPage @@ -152,7 +152,7 @@ class HassTabsSubpage extends LitElement { ${this.narrow ? html`
` : ""} - ${tabs.length > 1 || !this.narrow + ${showTabs ? html`
${tabs} @@ -163,10 +163,15 @@ class HassTabsSubpage extends LitElement {
-
+
-
+
+ +
`; } @@ -274,12 +279,13 @@ class HassTabsSubpage extends LitElement { margin-left: env(safe-area-inset-left); margin-right: env(safe-area-inset-right); height: calc(100% - 65px); + height: calc(100% - 65px - env(safe-area-inset-bottom)); overflow-y: auto; overflow: auto; -webkit-overflow-scrolling: touch; } - :host([narrow]) .content { + :host([narrow]) .content.tabs { height: calc(100% - 128px); height: calc(100% - 128px - env(safe-area-inset-bottom)); } @@ -290,7 +296,7 @@ class HassTabsSubpage extends LitElement { bottom: calc(16px + env(safe-area-inset-bottom)); z-index: 1; } - :host([narrow]) #fab { + :host([narrow]) #fab.tabs { bottom: calc(84px + env(safe-area-inset-bottom)); } #fab[is-wide] { From b9699f745f8acb057e91b8719d1529049edaf2d5 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 10 Sep 2020 13:50:24 -0500 Subject: [PATCH 09/41] Avoid watching all states in the default template (#6918) --- .../developer-tools/template/developer-tools-template.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/panels/developer-tools/template/developer-tools-template.ts b/src/panels/developer-tools/template/developer-tools-template.ts index 0bb9d0f78e..fbc5e03b63 100644 --- a/src/panels/developer-tools/template/developer-tools-template.ts +++ b/src/panels/developer-tools/template/developer-tools-template.ts @@ -31,10 +31,9 @@ The temperature is {{ my_test_json.temperature }} {{ my_test_json.unit }}. The sun will rise at {{ as_timestamp(strptime(state_attr("sun.sun", "next_rising"), "")) | timestamp_local }}. {%- endif %} -For loop example getting 3 entity values: +For loop example getting entity values in the weather domain: -{% for states in states | slice(3) -%} - {% set state = states | first %} +{% for state in states.weather -%} {%- if loop.first %}The {% elif loop.last %} and the {% else %}, the {% endif -%} {{ state.name | lower }} is {{state.state_with_unit}} {%- endfor %}.`; From 092a02a624e1c30308c67002df6dd26eecf2b93b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20S=C3=B8rensen?= Date: Thu, 10 Sep 2020 20:51:23 +0200 Subject: [PATCH 10/41] Convert ha-long-lived-access-tokens-card (#6917) Co-authored-by: Zack Barett Co-authored-by: Bram Kragten --- src/data/refresh_token.ts | 17 ++ src/dialogs/generic/dialog-box.ts | 23 ++- .../ha-long-lived-access-tokens-card.js | 171 ---------------- .../ha-long-lived-access-tokens-card.ts | 193 ++++++++++++++++++ src/panels/profile/ha-panel-profile.ts | 11 +- src/translations/en.json | 7 +- 6 files changed, 238 insertions(+), 184 deletions(-) create mode 100644 src/data/refresh_token.ts delete mode 100644 src/panels/profile/ha-long-lived-access-tokens-card.js create mode 100644 src/panels/profile/ha-long-lived-access-tokens-card.ts diff --git a/src/data/refresh_token.ts b/src/data/refresh_token.ts new file mode 100644 index 0000000000..bc2752bdf0 --- /dev/null +++ b/src/data/refresh_token.ts @@ -0,0 +1,17 @@ +declare global { + interface HASSDomEvents { + "hass-refresh-tokens": undefined; + } +} + +export interface RefreshToken { + client_icon?: string; + client_id: string; + client_name?: string; + created_at: string; + id: string; + is_current: boolean; + last_used_at?: string; + last_used_ip?: string; + type: "normal" | "long_lived_access_token"; +} diff --git a/src/dialogs/generic/dialog-box.ts b/src/dialogs/generic/dialog-box.ts index 7d63d3dd53..5df7cb4f1e 100644 --- a/src/dialogs/generic/dialog-box.ts +++ b/src/dialogs/generic/dialog-box.ts @@ -57,7 +57,8 @@ class DialogBox extends LitElement { open ?scrimClickAction=${this._params.prompt} ?escapeKeyAction=${this._params.prompt} - @closed=${this._dismiss} + @closed=${this._dialogClosed} + defaultAction="ignore" .heading=${this._params.title ? this._params.title : this._params.confirmation && @@ -78,10 +79,10 @@ class DialogBox extends LitElement { ${this._params.prompt ? html` `} - + ${this._params.confirmText ? this._params.confirmText : this.hass.localize("ui.dialogs.generic.ok")} @@ -133,7 +138,17 @@ class DialogBox extends LitElement { this._close(); } + private _dialogClosed(ev) { + if (ev.detail.action === "ignore") { + return; + } + this.closeDialog(); + } + private _close(): void { + if (!this._params) { + return; + } this._params = undefined; fireEvent(this, "dialog-closed", { dialog: this.localName }); } diff --git a/src/panels/profile/ha-long-lived-access-tokens-card.js b/src/panels/profile/ha-long-lived-access-tokens-card.js deleted file mode 100644 index 5943164610..0000000000 --- a/src/panels/profile/ha-long-lived-access-tokens-card.js +++ /dev/null @@ -1,171 +0,0 @@ -import "@material/mwc-button"; -import { html } from "@polymer/polymer/lib/utils/html-tag"; -/* eslint-plugin-disable lit */ -import { PolymerElement } from "@polymer/polymer/polymer-element"; -import { formatDateTime } from "../../common/datetime/format_date_time"; -import "../../components/ha-card"; -import "../../components/ha-icon-button"; -import { - showAlertDialog, - showPromptDialog, - showConfirmationDialog, -} from "../../dialogs/generic/show-dialog-box"; -import { EventsMixin } from "../../mixins/events-mixin"; -import LocalizeMixin from "../../mixins/localize-mixin"; -import "../../styles/polymer-ha-style"; -import "../../components/ha-settings-row"; - -/* - * @appliesMixin EventsMixin - * @appliesMixin LocalizeMixin - */ -class HaLongLivedTokens extends LocalizeMixin(EventsMixin(PolymerElement)) { - static get template() { - return html` - - -
-

- [[localize('ui.panel.profile.long_lived_access_tokens.description')]] - - [[localize('ui.panel.profile.long_lived_access_tokens.learn_auth_requests')]] - -

- -
- -
- - [[localize('ui.panel.profile.long_lived_access_tokens.create')]] - -
-
- `; - } - - static get properties() { - return { - hass: Object, - refreshTokens: Array, - _tokens: { - type: Array, - computed: "_computeTokens(refreshTokens)", - }, - }; - } - - _computeTokens(refreshTokens) { - return refreshTokens - .filter((tkn) => tkn.type === "long_lived_access_token") - .reverse(); - } - - _formatTitle(name) { - return this.localize( - "ui.panel.profile.long_lived_access_tokens.token_title", - "name", - name - ); - } - - _formatCreatedAt(created) { - return this.localize( - "ui.panel.profile.long_lived_access_tokens.created_at", - "date", - formatDateTime(new Date(created), this.hass.language) - ); - } - - async _handleCreate() { - const name = await showPromptDialog(this, { - text: this.localize( - "ui.panel.profile.long_lived_access_tokens.prompt_name" - ), - }); - if (!name) return; - try { - const token = await this.hass.callWS({ - type: "auth/long_lived_access_token", - lifespan: 3650, - client_name: name, - }); - await showPromptDialog(this, { - title: name, - text: this.localize( - "ui.panel.profile.long_lived_access_tokens.prompt_copy_token" - ), - defaultValue: token, - }); - this.fire("hass-refresh-tokens"); - } catch (err) { - // eslint-disable-next-line - console.error(err); - showAlertDialog(this, { - text: this.localize( - "ui.panel.profile.long_lived_access_tokens.create_failed" - ), - }); - } - } - - async _handleDelete(ev) { - const token = ev.model.item; - if ( - !(await showConfirmationDialog(this, { - text: this.localize( - "ui.panel.profile.long_lived_access_tokens.confirm_delete", - "name", - token.client_name - ), - })) - ) { - return; - } - try { - await this.hass.callWS({ - type: "auth/delete_refresh_token", - refresh_token_id: token.id, - }); - this.fire("hass-refresh-tokens"); - } catch (err) { - // eslint-disable-next-line - console.error(err); - showAlertDialog(this, { - text: this.localize( - "ui.panel.profile.long_lived_access_tokens.delete_failed" - ), - }); - } - } -} - -customElements.define("ha-long-lived-access-tokens-card", HaLongLivedTokens); diff --git a/src/panels/profile/ha-long-lived-access-tokens-card.ts b/src/panels/profile/ha-long-lived-access-tokens-card.ts new file mode 100644 index 0000000000..8c8eb36d96 --- /dev/null +++ b/src/panels/profile/ha-long-lived-access-tokens-card.ts @@ -0,0 +1,193 @@ +import "@material/mwc-button"; +import { + css, + CSSResultArray, + customElement, + html, + LitElement, + property, + TemplateResult, +} from "lit-element"; +import memoizeOne from "memoize-one"; +import relativeTime from "../../common/datetime/relative_time"; +import { fireEvent } from "../../common/dom/fire_event"; +import "../../components/ha-card"; +import "../../components/ha-icon-button"; +import "../../components/ha-settings-row"; +import { RefreshToken } from "../../data/refresh_token"; +import { + showAlertDialog, + showConfirmationDialog, + showPromptDialog, +} from "../../dialogs/generic/show-dialog-box"; +import { haStyle } from "../../resources/styles"; +import "../../styles/polymer-ha-style"; +import { HomeAssistant } from "../../types"; + +@customElement("ha-long-lived-access-tokens-card") +class HaLongLivedTokens extends LitElement { + @property({ attribute: false }) public hass!: HomeAssistant; + + @property({ attribute: false }) public refreshTokens?: RefreshToken[]; + + private _accessTokens = memoizeOne( + (refreshTokens: RefreshToken[]): RefreshToken[] => + refreshTokens + ?.filter((token) => token.type === "long_lived_access_token") + .reverse() + ); + + protected render(): TemplateResult { + const accessTokens = this._accessTokens(this.refreshTokens!); + + return html` + +
+ ${this.hass.localize( + "ui.panel.profile.long_lived_access_tokens.description" + )} + + + ${this.hass.localize( + "ui.panel.profile.long_lived_access_tokens.learn_auth_requests" + )} + + ${!accessTokens?.length + ? html`

+ ${this.hass.localize( + "ui.panel.profile.long_lived_access_tokens.empty_state" + )} +

` + : accessTokens!.map( + (token) => html` + ${token.client_name} +
+ ${this.hass.localize( + "ui.panel.profile.long_lived_access_tokens.created", + "date", + relativeTime( + new Date(token.created_at), + this.hass.localize + ) + )} +
+ +
` + )} +
+ +
+ + ${this.hass.localize( + "ui.panel.profile.long_lived_access_tokens.create" + )} + +
+
+ `; + } + + private async _createToken(): Promise { + const name = await showPromptDialog(this, { + text: this.hass.localize( + "ui.panel.profile.long_lived_access_tokens.prompt_name" + ), + inputLabel: this.hass.localize( + "ui.panel.profile.long_lived_access_tokens.name" + ), + }); + + if (!name) { + return; + } + + try { + const token = await this.hass.callWS({ + type: "auth/long_lived_access_token", + lifespan: 3650, + client_name: name, + }); + + showPromptDialog(this, { + title: name, + text: this.hass.localize( + "ui.panel.profile.long_lived_access_tokens.prompt_copy_token" + ), + defaultValue: token, + }); + + fireEvent(this, "hass-refresh-tokens"); + } catch (err) { + showAlertDialog(this, { + title: this.hass.localize( + "ui.panel.profile.long_lived_access_tokens.create_failed" + ), + text: err.message, + }); + } + } + + private async _deleteToken(ev: Event): Promise { + const token = (ev.currentTarget as any).token; + if ( + !(await showConfirmationDialog(this, { + text: this.hass.localize( + "ui.panel.profile.long_lived_access_tokens.confirm_delete", + "name", + token.client_name + ), + })) + ) { + return; + } + try { + await this.hass.callWS({ + type: "auth/delete_refresh_token", + refresh_token_id: token.id, + }); + fireEvent(this, "hass-refresh-tokens"); + } catch (err) { + await showAlertDialog(this, { + title: this.hass.localize( + "ui.panel.profile.long_lived_access_tokens.delete_failed" + ), + text: err.message, + }); + } + } + + static get styles(): CSSResultArray { + return [ + haStyle, + css` + ha-settings-row { + padding: 0; + } + a { + color: var(--primary-color); + } + ha-icon-button { + color: var(--primary-text-color); + } + `, + ]; + } +} + +declare global { + interface HTMLElementTagNameMap { + "ha-long-lived-access-tokens-card": HaLongLivedTokens; + } +} diff --git a/src/panels/profile/ha-panel-profile.ts b/src/panels/profile/ha-panel-profile.ts index 3c429e2e31..b4aa386092 100644 --- a/src/panels/profile/ha-panel-profile.ts +++ b/src/panels/profile/ha-panel-profile.ts @@ -1,5 +1,4 @@ import "@material/mwc-button"; -import "../../layouts/ha-app-layout"; import "@polymer/app-layout/app-header/app-header"; import "@polymer/app-layout/app-toolbar/app-toolbar"; import "@polymer/paper-item/paper-item"; @@ -9,9 +8,9 @@ import { css, CSSResultArray, html, + internalProperty, LitElement, property, - internalProperty, TemplateResult, } from "lit-element"; import { fireEvent } from "../../common/dom/fire_event"; @@ -22,7 +21,9 @@ import { CoreFrontendUserData, getOptimisticFrontendUserDataCollection, } from "../../data/frontend"; +import { RefreshToken } from "../../data/refresh_token"; import { showConfirmationDialog } from "../../dialogs/generic/show-dialog-box"; +import "../../layouts/ha-app-layout"; import { haStyle } from "../../resources/styles"; import { HomeAssistant } from "../../types"; import "./ha-advanced-mode-row"; @@ -35,15 +36,15 @@ import "./ha-pick-language-row"; import "./ha-pick-theme-row"; import "./ha-push-notifications-row"; import "./ha-refresh-tokens-card"; -import "./ha-set-vibrate-row"; import "./ha-set-suspend-row"; +import "./ha-set-vibrate-row"; class HaPanelProfile extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; - @property() public narrow!: boolean; + @property({ type: Boolean }) public narrow!: boolean; - @internalProperty() private _refreshTokens?: unknown[]; + @internalProperty() private _refreshTokens?: RefreshToken[]; @internalProperty() private _coreUserData?: CoreFrontendUserData | null; diff --git a/src/translations/en.json b/src/translations/en.json index 24f05139cc..29cc400560 100755 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -2512,14 +2512,13 @@ "header": "Long-Lived Access Tokens", "description": "Create long-lived access tokens to allow your scripts to interact with your Home Assistant instance. Each token will be valid for 10 years from creation. The following long-lived access tokens are currently active.", "learn_auth_requests": "Learn how to make authenticated requests.", - "created_at": "Created at {date}", - "last_used": "Last used at {date} from {location}", - "not_used": "Has never been used", + "created": "Created {date}", "confirm_delete": "Are you sure you want to delete the access token for {name}?", "delete_failed": "Failed to delete the access token.", "create": "Create Token", "create_failed": "Failed to create the access token.", - "prompt_name": "Name?", + "name": "Name", + "prompt_name": "Give the token a name", "prompt_copy_token": "Copy your access token. It will not be shown again.", "empty_state": "You have no long-lived access tokens yet." } From c06357a351377906b57bf6fd8641292d2646f8d6 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Thu, 10 Sep 2020 21:47:18 +0200 Subject: [PATCH 11/41] Only show what triggered a change if it wasn't a user (#6919) * Only show what triggered a change if it wasn't a user * Update ha-logbook.ts --- src/panels/logbook/ha-logbook.ts | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/panels/logbook/ha-logbook.ts b/src/panels/logbook/ha-logbook.ts index 33269319b6..e94fd7c72d 100644 --- a/src/panels/logbook/ha-logbook.ts +++ b/src/panels/logbook/ha-logbook.ts @@ -149,20 +149,23 @@ class HaLogbook extends LitElement { > ` : ""} - ${item.message} - ${item_username ? ` (${item_username})` : ``} - ${!item.context_event_type + ${item.message} + ${item_username + ? ` by ${item_username}` + : !item.context_event_type ? "" : item.context_event_type === "call_service" ? // Service Call - html` by service + ` by service ${item.context_domain}.${item.context_service}` : item.context_entity_id === item.entity_id ? // HomeKit or something that self references - html` by - ${item.context_name - ? item.context_name - : item.context_event_type}` + ` by + ${ + item.context_name + ? item.context_name + : item.context_event_type + }` : // Another entity such as an automation or script html` by Date: Thu, 10 Sep 2020 22:04:16 +0200 Subject: [PATCH 12/41] Ignore disconnect codes for shutdown and reboot (#6901) Co-authored-by: Bram Kragten --- hassio/src/dashboard/hassio-update.ts | 3 ++- hassio/src/system/hassio-host-info.ts | 27 ++++++++++++++++++--------- src/data/hassio/common.ts | 2 ++ 3 files changed, 22 insertions(+), 10 deletions(-) diff --git a/hassio/src/dashboard/hassio-update.ts b/hassio/src/dashboard/hassio-update.ts index 9bb6cc73ec..eec4097e54 100644 --- a/hassio/src/dashboard/hassio-update.ts +++ b/hassio/src/dashboard/hassio-update.ts @@ -16,6 +16,7 @@ import "../../../src/components/ha-svg-icon"; import { extractApiErrorMessage, HassioResponse, + ignoredStatusCodes, } from "../../../src/data/hassio/common"; import { HassioHassOSInfo } from "../../../src/data/hassio/host"; import { @@ -166,7 +167,7 @@ export class HassioUpdate extends LitElement { } catch (err) { // Only show an error if the status code was not expected (user behind proxy) // or no status at all(connection terminated) - if (err.status_code && ![502, 503, 504].includes(err.status_code)) { + if (err.status_code && !ignoredStatusCodes.has(err.status_code)) { showAlertDialog(this, { title: "Update failed", text: extractApiErrorMessage(err), diff --git a/hassio/src/system/hassio-host-info.ts b/hassio/src/system/hassio-host-info.ts index bdd3e801b4..5f44703ac3 100644 --- a/hassio/src/system/hassio-host-info.ts +++ b/hassio/src/system/hassio-host-info.ts @@ -19,7 +19,10 @@ import "../../../src/components/buttons/ha-progress-button"; import "../../../src/components/ha-button-menu"; import "../../../src/components/ha-card"; import "../../../src/components/ha-settings-row"; -import { extractApiErrorMessage } from "../../../src/data/hassio/common"; +import { + extractApiErrorMessage, + ignoredStatusCodes, +} from "../../../src/data/hassio/common"; import { fetchHassioHardwareInfo } from "../../../src/data/hassio/hardware"; import { changeHostOptions, @@ -245,10 +248,13 @@ class HassioHostInfo extends LitElement { try { await rebootHost(this.hass); } catch (err) { - showAlertDialog(this, { - title: "Failed to reboot", - text: extractApiErrorMessage(err), - }); + // Ignore connection errors, these are all expected + if (err.status_code && !ignoredStatusCodes.has(err.status_code)) { + showAlertDialog(this, { + title: "Failed to reboot", + text: extractApiErrorMessage(err), + }); + } } button.progress = false; } @@ -272,10 +278,13 @@ class HassioHostInfo extends LitElement { try { await shutdownHost(this.hass); } catch (err) { - showAlertDialog(this, { - title: "Failed to shutdown", - text: extractApiErrorMessage(err), - }); + // Ignore connection errors, these are all expected + if (err.status_code && !ignoredStatusCodes.has(err.status_code)) { + showAlertDialog(this, { + title: "Failed to shutdown", + text: extractApiErrorMessage(err), + }); + } } button.progress = false; } diff --git a/src/data/hassio/common.ts b/src/data/hassio/common.ts index 4ab432b855..ad315096a3 100644 --- a/src/data/hassio/common.ts +++ b/src/data/hassio/common.ts @@ -13,3 +13,5 @@ export const extractApiErrorMessage = (error: any): string => { : error.body || "Unknown error, see logs" : error; }; + +export const ignoredStatusCodes = new Set([502, 503, 504]); From aa4bc2ce03dd0b16b1d4567d2afcb39d6cb2f142 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Thu, 10 Sep 2020 22:40:54 +0200 Subject: [PATCH 13/41] Make logbook a bit smaller in more info (#6921) --- src/dialogs/more-info/ha-more-info-history.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dialogs/more-info/ha-more-info-history.ts b/src/dialogs/more-info/ha-more-info-history.ts index 50390b31a2..c75181a1b4 100644 --- a/src/dialogs/more-info/ha-more-info-history.ts +++ b/src/dialogs/more-info/ha-more-info-history.ts @@ -148,7 +148,7 @@ export class MoreInfoHistory extends LitElement { padding: 16px; } ha-logbook { - max-height: 360px; + max-height: 250px; overflow: auto; } ha-circular-progress { From 2139a80a7aa40b830d24924989f26636a19977ac Mon Sep 17 00:00:00 2001 From: Philip Allgaier Date: Thu, 10 Sep 2020 22:59:45 +0200 Subject: [PATCH 14/41] Use proper constants for "unavailable" checks (#6922) * Use proper constants for "unavailable" * Additional usage of constants --- src/common/entity/state_card_type.ts | 3 ++- src/components/entity/ha-state-label-badge.ts | 10 ++++++---- src/dialogs/more-info/controls/more-info-automation.ts | 5 +++-- src/panels/config/automation/ha-automation-picker.ts | 9 +++++---- src/panels/config/entities/ha-config-entities.ts | 3 ++- src/panels/lovelace/cards/hui-light-card.ts | 4 ++-- src/panels/lovelace/common/validate-condition.ts | 3 ++- src/panels/lovelace/components/hui-image.ts | 3 ++- .../entity-rows/hui-input-datetime-entity-row.ts | 6 +++--- .../lovelace/entity-rows/hui-sensor-entity-row.ts | 4 ++-- test-mocha/common/entity/compute_state_display.ts | 3 ++- 11 files changed, 31 insertions(+), 22 deletions(-) diff --git a/src/common/entity/state_card_type.ts b/src/common/entity/state_card_type.ts index a24994d5b3..2013ee2058 100644 --- a/src/common/entity/state_card_type.ts +++ b/src/common/entity/state_card_type.ts @@ -3,9 +3,10 @@ import { HomeAssistant } from "../../types"; import { DOMAINS_WITH_CARD } from "../const"; import { canToggleState } from "./can_toggle_state"; import { computeStateDomain } from "./compute_state_domain"; +import { UNAVAILABLE } from "../../data/entity"; export const stateCardType = (hass: HomeAssistant, stateObj: HassEntity) => { - if (stateObj.state === "unavailable") { + if (stateObj.state === UNAVAILABLE) { return "display"; } diff --git a/src/components/entity/ha-state-label-badge.ts b/src/components/entity/ha-state-label-badge.ts index b38009042f..3441979436 100644 --- a/src/components/entity/ha-state-label-badge.ts +++ b/src/components/entity/ha-state-label-badge.ts @@ -20,6 +20,7 @@ import { stateIcon } from "../../common/entity/state_icon"; import { timerTimeRemaining } from "../../common/entity/timer_time_remaining"; import { HomeAssistant } from "../../types"; import "../ha-label-badge"; +import { UNAVAILABLE, UNKNOWN } from "../../data/entity"; @customElement("ha-state-label-badge") export class HaStateLabelBadge extends LitElement { @@ -81,7 +82,8 @@ export class HaStateLabelBadge extends LitElement { ? "" : this.image ? this.image - : state.attributes.entity_picture_local || state.attributes.entity_picture}" + : state.attributes.entity_picture_local || + state.attributes.entity_picture}" .label="${this._computeLabel(domain, state, this._timerTimeRemaining)}" .description="${this.name ? this.name : computeStateName(state)}" > @@ -108,7 +110,7 @@ export class HaStateLabelBadge extends LitElement { return null; case "sensor": default: - return state.state === "unknown" + return state.state === UNKNOWN ? "-" : state.attributes.unit_of_measurement ? state.state @@ -121,7 +123,7 @@ export class HaStateLabelBadge extends LitElement { } private _computeIcon(domain: string, state: HassEntity) { - if (state.state === "unavailable") { + if (state.state === UNAVAILABLE) { return null; } switch (domain) { @@ -166,7 +168,7 @@ export class HaStateLabelBadge extends LitElement { private _computeLabel(domain, state, _timerTimeRemaining) { if ( - state.state === "unavailable" || + state.state === UNAVAILABLE || ["device_tracker", "alarm_control_panel", "person"].includes(domain) ) { // Localize the state with a special state_badge namespace, which has variations of diff --git a/src/dialogs/more-info/controls/more-info-automation.ts b/src/dialogs/more-info/controls/more-info-automation.ts index 8b4ff441c2..6a1a789b99 100644 --- a/src/dialogs/more-info/controls/more-info-automation.ts +++ b/src/dialogs/more-info/controls/more-info-automation.ts @@ -12,12 +12,13 @@ import { import "../../../components/ha-relative-time"; import { triggerAutomation } from "../../../data/automation"; import { HomeAssistant } from "../../../types"; +import { UNAVAILABLE_STATES } from "../../../data/entity"; @customElement("more-info-automation") class MoreInfoAutomation extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; - @property() public stateObj?: HassEntity; + @property({ attribute: false }) public stateObj?: HassEntity; protected render(): TemplateResult { if (!this.hass || !this.stateObj) { @@ -36,7 +37,7 @@ class MoreInfoAutomation extends LitElement {
${this.hass.localize("ui.card.automation.trigger")} diff --git a/src/panels/config/automation/ha-automation-picker.ts b/src/panels/config/automation/ha-automation-picker.ts index 221f7f1e14..d95e80f079 100644 --- a/src/panels/config/automation/ha-automation-picker.ts +++ b/src/panels/config/automation/ha-automation-picker.ts @@ -25,6 +25,7 @@ import { showAutomationEditor, triggerAutomation, } from "../../../data/automation"; +import { UNAVAILABLE_STATES } from "../../../data/entity"; import "../../../layouts/hass-tabs-subpage-data-table"; import { haStyle } from "../../../resources/styles"; import { HomeAssistant, Route } from "../../../types"; @@ -35,9 +36,9 @@ import { showThingtalkDialog } from "./show-dialog-thingtalk"; class HaAutomationPicker extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; - @property() public isWide!: boolean; + @property({ type: Boolean }) public isWide!: boolean; - @property() public narrow!: boolean; + @property({ type: Boolean }) public narrow!: boolean; @property() public route!: Route; @@ -58,7 +59,7 @@ class HaAutomationPicker extends LitElement { toggle: { title: "", type: "icon", - template: (_toggle, automation) => + template: (_toggle, automation: any) => html` this._execute(ev)} - .disabled=${automation.state === "unavailable"} + .disabled=${UNAVAILABLE_STATES.includes(automation.state)} > ${this.hass.localize("ui.card.automation.trigger")} diff --git a/src/panels/config/entities/ha-config-entities.ts b/src/panels/config/entities/ha-config-entities.ts index 8ac21ad076..366c852fa9 100644 --- a/src/panels/config/entities/ha-config-entities.ts +++ b/src/panels/config/entities/ha-config-entities.ts @@ -59,6 +59,7 @@ import { showEntityEditorDialog, } from "./show-dialog-entity-editor"; import { haStyle } from "../../../resources/styles"; +import { UNAVAILABLE } from "../../../data/entity"; export interface StateEntity extends EntityRegistryEntry { readonly?: boolean; @@ -281,7 +282,7 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) { for (const entry of entities) { const entity = this.hass.states[entry.entity_id]; - const unavailable = entity?.state === "unavailable"; + const unavailable = entity?.state === UNAVAILABLE; const restored = entity?.attributes.restored; if (!showUnavailable && unavailable) { diff --git a/src/panels/lovelace/cards/hui-light-card.ts b/src/panels/lovelace/cards/hui-light-card.ts index bb65f89e1c..f05a1c4c1c 100644 --- a/src/panels/lovelace/cards/hui-light-card.ts +++ b/src/panels/lovelace/cards/hui-light-card.ts @@ -20,7 +20,7 @@ import { computeStateName } from "../../../common/entity/compute_state_name"; import { stateIcon } from "../../../common/entity/state_icon"; import { supportsFeature } from "../../../common/entity/supports-feature"; import "../../../components/ha-card"; -import { UNAVAILABLE_STATES } from "../../../data/entity"; +import { UNAVAILABLE_STATES, UNAVAILABLE } from "../../../data/entity"; import { SUPPORT_BRIGHTNESS } from "../../../data/light"; import { ActionHandlerEvent } from "../../../data/lovelace"; import { HomeAssistant, LightEntity } from "../../../types"; @@ -133,7 +133,7 @@ export class HuiLightCard extends LitElement implements LovelaceCard { SUPPORT_BRIGHTNESS ), "state-on": stateObj.state === "on", - "state-unavailable": stateObj.state === "unavailable", + "state-unavailable": stateObj.state === UNAVAILABLE, })}" .icon=${this._config.icon || stateIcon(stateObj)} .disabled=${UNAVAILABLE_STATES.includes(stateObj.state)} diff --git a/src/panels/lovelace/common/validate-condition.ts b/src/panels/lovelace/common/validate-condition.ts index e50707d871..0aca5f2e11 100644 --- a/src/panels/lovelace/common/validate-condition.ts +++ b/src/panels/lovelace/common/validate-condition.ts @@ -1,4 +1,5 @@ import { HomeAssistant } from "../../../types"; +import { UNAVAILABLE } from "../../../data/entity"; export interface Condition { entity: string; @@ -13,7 +14,7 @@ export function checkConditionsMet( return conditions.every((c) => { const state = hass.states[c.entity] ? hass!.states[c.entity].state - : "unavailable"; + : UNAVAILABLE; return c.state ? state === c.state : state !== c.state_not; }); diff --git a/src/panels/lovelace/components/hui-image.ts b/src/panels/lovelace/components/hui-image.ts index 115e118f83..004bf705ff 100644 --- a/src/panels/lovelace/components/hui-image.ts +++ b/src/panels/lovelace/components/hui-image.ts @@ -17,6 +17,7 @@ import parseAspectRatio from "../../../common/util/parse-aspect-ratio"; import "../../../components/ha-camera-stream"; import { fetchThumbnailUrlWithCache } from "../../../data/camera"; import { CameraEntity, HomeAssistant } from "../../../types"; +import { UNAVAILABLE } from "../../../data/entity"; const UPDATE_INTERVAL = 10000; const DEFAULT_FILTER = "grayscale(100%)"; @@ -73,7 +74,7 @@ export class HuiImage extends LitElement { } const ratio = this.aspectRatio ? parseAspectRatio(this.aspectRatio) : null; const stateObj = this.entity ? this.hass.states[this.entity] : undefined; - const state = stateObj ? stateObj.state : "unavailable"; + const state = stateObj ? stateObj.state : UNAVAILABLE; // Figure out image source to use let imageSrc: string | undefined; diff --git a/src/panels/lovelace/entity-rows/hui-input-datetime-entity-row.ts b/src/panels/lovelace/entity-rows/hui-input-datetime-entity-row.ts index f878c33985..46e5a1e88c 100644 --- a/src/panels/lovelace/entity-rows/hui-input-datetime-entity-row.ts +++ b/src/panels/lovelace/entity-rows/hui-input-datetime-entity-row.ts @@ -11,7 +11,7 @@ import "../../../components/ha-date-input"; import type { HaDateInput } from "../../../components/ha-date-input"; import "../../../components/paper-time-input"; import type { PaperTimeInput } from "../../../components/paper-time-input"; -import { UNAVAILABLE_STATES } from "../../../data/entity"; +import { UNAVAILABLE_STATES, UNKNOWN } from "../../../data/entity"; import { setInputDateTimeValue } from "../../../data/input_datetime"; import type { HomeAssistant } from "../../../types"; import { hasConfigOrEntityChanged } from "../common/has-changed"; @@ -70,10 +70,10 @@ class HuiInputDatetimeEntityRow extends LitElement implements LovelaceRow { ? html` ${stateObj.attributes.device_class === SENSOR_DEVICE_CLASS_TIMESTAMP && - stateObj.state !== "unavailable" && - stateObj.state !== "unknown" + !UNAVAILABLE_STATES.includes(stateObj.state) ? html` { // Mock Localize function for testing @@ -72,7 +73,7 @@ describe("computeStateDisplay", () => { }; const stateObj: any = { entity_id: "sensor.test", - state: "unknown", + state: UNKNOWN, attributes: { unit_of_measurement: "m", }, From b0508f430ee026219f9e0cc4d0ee085cb5aa1ab4 Mon Sep 17 00:00:00 2001 From: HomeAssistant Azure Date: Fri, 11 Sep 2020 00:32:26 +0000 Subject: [PATCH 15/41] [ci skip] Translation update --- translations/frontend/ca.json | 4 +++ translations/frontend/cs.json | 4 +++ translations/frontend/en.json | 4 ++- translations/frontend/it.json | 4 +++ translations/frontend/nb.json | 10 ++++-- translations/frontend/nl.json | 57 +++++++++++++++++++++++++----- translations/frontend/ru.json | 3 ++ translations/frontend/zh-Hant.json | 4 +++ 8 files changed, 77 insertions(+), 13 deletions(-) diff --git a/translations/frontend/ca.json b/translations/frontend/ca.json index 61e6b7e55d..0e0d3b89ec 100644 --- a/translations/frontend/ca.json +++ b/translations/frontend/ca.json @@ -567,6 +567,9 @@ "loading_history": "Carregant historial d'estats...", "no_history_found": "No s'ha trobat cap historial d'estats." }, + "logbook": { + "entries_not_found": "No s'han trobat entrades al registre." + }, "media-browser": { "audio_not_supported": "El teu navegador no és compatible amb l'element d'àudio.", "choose_player": "Tria el reproductor", @@ -703,6 +706,7 @@ }, "more_info_control": { "controls": "Controls", + "details": "Detalls", "dismiss": "Desestimar el diàleg", "edit": "Edita entitat", "history": "Historial", diff --git a/translations/frontend/cs.json b/translations/frontend/cs.json index dec6bdf715..17235e792e 100644 --- a/translations/frontend/cs.json +++ b/translations/frontend/cs.json @@ -567,6 +567,9 @@ "loading_history": "Historie stavu se načítá...", "no_history_found": "Historie stavu chybí." }, + "logbook": { + "entries_not_found": "Nenalezeny žádné záznamy." + }, "media-browser": { "audio_not_supported": "Váš prohlížeč nepodporuje element \"audio\".", "choose_player": "Vyberte přehrávač", @@ -703,6 +706,7 @@ }, "more_info_control": { "controls": "Ovládací prvky", + "details": "Podrobnosti", "dismiss": "Zavřít dialog", "edit": "Upravit entitu", "history": "Historie", diff --git a/translations/frontend/en.json b/translations/frontend/en.json index 180e20eb4c..302930a05a 100644 --- a/translations/frontend/en.json +++ b/translations/frontend/en.json @@ -2963,6 +2963,7 @@ "confirm_delete": "Are you sure you want to delete the access token for {name}?", "create": "Create Token", "create_failed": "Failed to create the access token.", + "created": "Created {date}", "created_at": "Created at {date}", "delete_failed": "Failed to delete the access token.", "description": "Create long-lived access tokens to allow your scripts to interact with your Home Assistant instance. Each token will be valid for 10 years from creation. The following long-lived access tokens are currently active.", @@ -2970,9 +2971,10 @@ "header": "Long-Lived Access Tokens", "last_used": "Last used at {date} from {location}", "learn_auth_requests": "Learn how to make authenticated requests.", + "name": "Name", "not_used": "Has never been used", "prompt_copy_token": "Copy your access token. It will not be shown again.", - "prompt_name": "Name?" + "prompt_name": "Give the token a name" }, "mfa_setup": { "close": "Close", diff --git a/translations/frontend/it.json b/translations/frontend/it.json index fa2f1fb908..c7976293f9 100644 --- a/translations/frontend/it.json +++ b/translations/frontend/it.json @@ -567,6 +567,9 @@ "loading_history": "Caricamento storico...", "no_history_found": "Nessuno storico trovato." }, + "logbook": { + "entries_not_found": "Non sono state trovate voci nel registro." + }, "media-browser": { "audio_not_supported": "Il tuo browser non supporta l'elemento audio.", "choose_player": "Scegli il lettore", @@ -703,6 +706,7 @@ }, "more_info_control": { "controls": "Controlli", + "details": "Dettagli", "dismiss": "Chiudi finestra di dialogo", "edit": "Modifica entità", "history": "Storico", diff --git a/translations/frontend/nb.json b/translations/frontend/nb.json index c01cf5a5a5..ab4abc249b 100644 --- a/translations/frontend/nb.json +++ b/translations/frontend/nb.json @@ -567,6 +567,9 @@ "loading_history": "Laster statushistorikk...", "no_history_found": "Ingen statushistorikk funnet." }, + "logbook": { + "entries_not_found": "Ingen loggbokoppføringer funnet." + }, "media-browser": { "audio_not_supported": "Nettleseren din støtter ikke lydelementet.", "choose_player": "Velg spiller", @@ -703,6 +706,7 @@ }, "more_info_control": { "controls": "Kontroller", + "details": "Detaljer", "dismiss": "Avvis dialogboksen", "edit": "Redigér entitet", "history": "Historie", @@ -1788,7 +1792,7 @@ "nodeplusinfo": "Innhenting av Z-Wave + informasjon fra noden", "probe": "Kontrollere om noden er våken/levende", "protocolinfo": "Få grunnleggende Z-Wave-funksjoner i denne noden fra kontrolleren", - "session": "\nFå sjelden endrede verdier fra noden", + "session": "Henter verdier fra noden som sjeldent oppdateres", "static": "Innhenting av statiske verdier fra enheten", "versions": "Hente informasjon om fastvare- og kommandoklasseversjoner", "wakeup": "Sette opp støtte for vekkingskøer og meldinger" @@ -2463,7 +2467,7 @@ "name": "Entitetsfilter" }, "entity": { - "description": "Enhetskortet gir deg en rask oversikt over enhetens tilstand.", + "description": "Entitetskortet gir deg en rask oversikt over entitetens tilstand.", "name": "Entitet" }, "gauge": { @@ -2523,7 +2527,7 @@ "name": "Horisontal Stabel" }, "humidifier": { - "description": "Luftfukter kortet gir kontroll over luftfukter enheten din. Lar deg endre fuktigheten og modusen til enheten.", + "description": "Luftfukter kortet gir kontroll over luftfukter entiteten din. Lar deg endre fuktigheten og modusen til entiteten.", "name": "Luftfukter" }, "iframe": { diff --git a/translations/frontend/nl.json b/translations/frontend/nl.json index 20dec55c5e..7e36879dae 100644 --- a/translations/frontend/nl.json +++ b/translations/frontend/nl.json @@ -19,6 +19,7 @@ "logbook": "Logboek", "mailbox": "Postvak", "map": "Kaart", + "media_browser": "Mediabrowser", "profile": "Profiel", "shopping_list": "Boodschappenlijst", "states": "Overzicht" @@ -552,6 +553,10 @@ "toggle": "Omschakelen" }, "entity": { + "entity-attribute-picker": { + "attribute": "Attribuut", + "show_attributes": "Toon attributen" + }, "entity-picker": { "clear": "Wis", "entity": "Entiteit", @@ -562,7 +567,11 @@ "loading_history": "Geschiedenis laden ...", "no_history_found": "Geen geschiedenis gevonden" }, + "logbook": { + "entries_not_found": "Geen logboekvermeldingen gevonden." + }, "media-browser": { + "audio_not_supported": "Uw browser ondersteunt het audio-element niet.", "choose_player": "Kies speler", "choose-source": "Kies bron", "content-type": { @@ -572,6 +581,7 @@ "playlist": "Afspeellijst", "server": "Server" }, + "media_browsing_error": "Fout bij bladeren door media", "media_not_supported": "De Browser Media Player ondersteunt dit type media niet", "media_player": "Mediaspeler", "media-player-browser": "Mediaspeler-browser", @@ -696,6 +706,7 @@ }, "more_info_control": { "controls": "Besturing", + "details": "Details", "dismiss": "Dialoogvenster sluiten", "edit": "Entiteit bewerken", "history": "Geschiedenis", @@ -927,7 +938,9 @@ "service_data": "Service data" }, "wait_for_trigger": { - "label": "Wacht op trigger" + "continue_timeout": "Ga verder op time-out", + "label": "Wacht op trigger", + "timeout": "Time-out (optioneel)" }, "wait_template": { "continue_timeout": "Ga verder op time-out", @@ -994,7 +1007,9 @@ "time": { "after": "Nadat", "before": "Voordat", - "label": "Tijd" + "label": "Tijd", + "type_input": "Waarde van een datum/tijdhelper", + "type_value": "Vaste tijd" }, "zone": { "entity": "Entiteit met locatie", @@ -1082,6 +1097,7 @@ "value_template": "Waardesjabloon (optioneel)" }, "state": { + "attribute": "Attribuut (Optioneel)", "for": "Voor", "from": "Van", "label": "Staat", @@ -1108,8 +1124,10 @@ "seconds": "Seconden" }, "time": { - "at": "Om", - "label": "Tijd" + "at": "Op tijd", + "label": "Tijd", + "type_input": "Waarde van een datum/tijdhelper", + "type_value": "Vaste tijd" }, "webhook": { "label": "Webhook", @@ -1132,6 +1150,8 @@ "add_automation": "Automatisering toevoegen", "delete_automation": "Verwijder automatisering", "delete_confirm": "Weet je zeker dat je deze automatisering wilt verwijderen?", + "duplicate": "Dupliceren", + "duplicate_automation": "Dupliceer automatisering", "edit_automation": "Bewerk automatisering", "header": "Automatiseringsbewerker", "headers": { @@ -1216,9 +1236,11 @@ "dont_expose_entity": "Entiteit niet beschikbaar maken", "expose": "Blootstellen aan Alexa", "expose_entity": "Enititeit beschikbaar maken", + "exposed": "{selected} blootgesteld", "exposed_entities": "Blootgestelde entiteiten", "follow_domain": "Domein volgen", "manage_domains": "Beheer domeinen", + "not_exposed": "{selected} niet blootgesteld", "not_exposed_entities": "Niet blootgestelde entiteiten", "title": "Alexa" }, @@ -1259,8 +1281,11 @@ "dont_expose_entity": "Entiteit niet beschikbaar maken", "expose": "Blootstellen aan Google Assistant", "expose_entity": "Enititeit beschikbaar maken", + "exposed": "{selected} blootgesteld", "exposed_entities": "Blootgestelde entiteiten", + "follow_domain": "Domein volgen", "manage_domains": "Beheer domeinen", + "not_exposed": "{selected} niet blootgesteld", "not_exposed_entities": "Niet blootgestelde entiteiten", "sync_to_google": "Wijzigingen synchroniseren met Google.", "title": "Google Assistant" @@ -1551,6 +1576,7 @@ "reload_restart_confirm": "Start Home Assistant opnieuw om het opnieuw laden van deze integratie te voltooien", "rename": "Naam wijzigen", "restart_confirm": "Herstart Home Assistant om het verwijderen van deze integratie te voltooien", + "services": "{count} {count, plural,\n one {service}\n other {services}\n}", "settings_button": "Instellingen bewerken voor {integration}", "system_options": "Systeeminstellingen", "system_options_button": "Systeeminstellingen voor {integration}", @@ -1771,11 +1797,21 @@ "versions": "Het verkrijgen van informatie over firmware en commandoklasseversies", "wakeup": "Ondersteuning instellen voor waakwachtrijen en berichten" }, + "node": { + "button": "Node details", + "not_found": "Node niet gevonden" + }, "nodes_table": { - "failed": "Mislukt" + "failed": "Mislukt", + "id": "ID", + "manufacturer": "Fabrikant", + "model": "Model", + "query_stage": "Vraagfase", + "zwave_plus": "Z-Wave Plus" }, "refresh_node": { "battery_note": "Als het knooppunt op batterijen werkt, moet u het uit de sluimerstand halen voordat u verder gaat", + "button": "Node verversen", "complete": "Knooppunt vernieuwen voltooid", "description": "Dit zal OpenZWave vertellen om een knooppunt opnieuw te ondervragen en de opdrachtklassen, mogelijkheden en waarden van het knooppunt bij te werken.", "node_status": "Knooppuntstatus", @@ -1924,7 +1960,7 @@ "filter": "Herlaad filter-entiteiten", "generic": "Herlaad generieke IP camera entiteiten", "generic_thermostat": "Herlaad generieke thermostaat entiteiten", - "group": "Herlaad groepen", + "group": "Herlaad groepen, groepsentiteiten en meld services", "heading": "Configuratie herladen", "history_stats": "Herlaad historische statistieken entiteiten", "homekit": "Herlaad HomeKit", @@ -1938,7 +1974,8 @@ "mqtt": "Herlaad mqtt entiteiten", "person": "Herlaad personen", "ping": "Herlaad ping binaire sensor entiteiten", - "rest": "Herlaad rust-entiteiten", + "reload": "Herlaad {domain}", + "rest": "Laad resterende entiteiten opnieuw en stel services op de hoogte", "rpi_gpio": "Herlaad Raspberry Pi GPIO-entiteiten", "scene": "Herlaad scenes", "script": "Herlaad scripts", @@ -2033,7 +2070,7 @@ "system": "Systeem" } }, - "users_privileges_note": "Gebruikersgroepen zijn nog in ontwikkeling. De gebruiker kan de instantie niet beheren via de interface. We zijn bezig met het controleren van de API-eindpunten toegang voor beheerders." + "users_privileges_note": "Gebruikersgroepen zijn nog in ontwikkeling. De gebruiker kan de instantie niet beheren via de interface. We controleren nog steeds alle beheer API eindpunten om ervoor te zorgen dat ze de toegang tot beheerders correct beperken." }, "zha": { "add_device_page": { @@ -2217,7 +2254,7 @@ "node_to_control": "Knooppunt om te besturen", "nodes": "Knooppunten", "nodes_hint": "Selecteer knooppunt om de opties per knooppunt te bekijken", - "nodes_in_group": "Andere knooppunten in deze groep:", + "nodes_in_group": "Andere nodes in deze groep:", "pooling_intensity": "Polling intensiteit", "protection": "Bescherming", "remove_broadcast": "Broadcast verwijderen", @@ -2565,6 +2602,8 @@ } }, "cardpicker": { + "by_card": "Per kaart", + "by_entity": "Per entiteit", "custom_card": "Aangepaste", "domain": "Domein", "entity": "Entiteit", diff --git a/translations/frontend/ru.json b/translations/frontend/ru.json index 0ff93c88b1..7122ac3008 100644 --- a/translations/frontend/ru.json +++ b/translations/frontend/ru.json @@ -567,6 +567,9 @@ "loading_history": "Загрузка истории...", "no_history_found": "История не найдена." }, + "logbook": { + "entries_not_found": "В журнале нет записей." + }, "media-browser": { "audio_not_supported": "Ваш браузер не поддерживает аудио.", "choose_player": "Выберите медиаплеер", diff --git a/translations/frontend/zh-Hant.json b/translations/frontend/zh-Hant.json index 67203a2c85..0fcc924359 100644 --- a/translations/frontend/zh-Hant.json +++ b/translations/frontend/zh-Hant.json @@ -567,6 +567,9 @@ "loading_history": "正在載入狀態歷史...", "no_history_found": "找不到狀態歷史。" }, + "logbook": { + "entries_not_found": "找不到實體日誌。" + }, "media-browser": { "audio_not_supported": "瀏覽器不支援音效元件。", "choose_player": "選擇播放器", @@ -703,6 +706,7 @@ }, "more_info_control": { "controls": "控制", + "details": "詳細資訊", "dismiss": "忽略對話", "edit": "編輯實體", "history": "歷史", From ce8ee569c4ab304d3d42779abd1aa05f51e05fba Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Fri, 11 Sep 2020 10:17:05 +0200 Subject: [PATCH 16/41] Check if history and logbook are loaded (#6908) --- src/dialogs/more-info/ha-more-info-dialog.ts | 3 ++- src/dialogs/more-info/ha-more-info-history.ts | 27 +++++++++++++------ 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/src/dialogs/more-info/ha-more-info-dialog.ts b/src/dialogs/more-info/ha-more-info-dialog.ts index 15d71e694f..f5beadea90 100644 --- a/src/dialogs/more-info/ha-more-info-dialog.ts +++ b/src/dialogs/more-info/ha-more-info-dialog.ts @@ -223,7 +223,8 @@ export class MoreInfoDialog extends LitElement { private _computeShowHistoryComponent(entityId) { return ( - isComponentLoaded(this.hass, "history") && + (isComponentLoaded(this.hass, "history") || + isComponentLoaded(this.hass, "logbook")) && !DOMAINS_MORE_INFO_NO_HISTORY.includes(computeDomain(entityId)) ); } diff --git a/src/dialogs/more-info/ha-more-info-history.ts b/src/dialogs/more-info/ha-more-info-history.ts index c75181a1b4..f436a9ddab 100644 --- a/src/dialogs/more-info/ha-more-info-history.ts +++ b/src/dialogs/more-info/ha-more-info-history.ts @@ -8,6 +8,7 @@ import { PropertyValues, TemplateResult, } from "lit-element"; +import { isComponentLoaded } from "../../common/config/is_component_loaded"; import { computeStateDomain } from "../../common/entity/compute_state_domain"; import "../../components/ha-circular-progress"; import "../../components/state-history-charts"; @@ -42,13 +43,16 @@ export class MoreInfoHistory extends LitElement { return html``; } - return html` - ${!this._entries + return html`${isComponentLoaded(this.hass, "history") + ? html`` + : ""} + ${isComponentLoaded(this.hass, "logbook") + ? !this._entries ? html` ${this.hass.localize("ui.components.logbook.entries_not_found")} -
`}`; +
` + : ""} `; } protected firstUpdated(): void { @@ -97,6 +102,9 @@ export class MoreInfoHistory extends LitElement { } private async _getStateHistory(): Promise { + if (!isComponentLoaded(this.hass, "history")) { + return; + } this._stateHistory = await getRecentWithCache( this.hass!, this.entityId, @@ -111,6 +119,9 @@ export class MoreInfoHistory extends LitElement { } private async _getLogBookData() { + if (!isComponentLoaded(this.hass, "logbook")) { + return; + } const yesterday = new Date(new Date().getTime() - 24 * 60 * 60 * 1000); const now = new Date(); this._entries = await getLogbookData( From 5af4ce28abbabc7de4cf2a0ca8dce6883d136f2c Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Fri, 11 Sep 2020 14:42:39 +0200 Subject: [PATCH 17/41] Restrict long press to header of sidebar (#6933) --- src/components/ha-sidebar.ts | 32 ++++++++++---------------------- 1 file changed, 10 insertions(+), 22 deletions(-) diff --git a/src/components/ha-sidebar.ts b/src/components/ha-sidebar.ts index 41d460fc9f..07508cfe3e 100644 --- a/src/components/ha-sidebar.ts +++ b/src/components/ha-sidebar.ts @@ -30,7 +30,6 @@ import memoizeOne from "memoize-one"; import { LocalStorage } from "../common/decorators/local-storage"; import { fireEvent } from "../common/dom/fire_event"; import { computeDomain } from "../common/entity/compute_domain"; -import { navigate } from "../common/navigate"; import { compare } from "../common/string/compare"; import { computeRTL } from "../common/util/compute_rtl"; import { ActionHandlerDetail } from "../data/lovelace"; @@ -235,7 +234,14 @@ class HaSidebar extends LitElement { ` : ""} - `; @@ -189,6 +240,11 @@ ${this._processed} { - this._processed = result; + this._templateResult = result; + this._error = undefined; }, { template: this._template, @@ -230,9 +287,10 @@ ${this._processed}; @@ -85,7 +88,7 @@ export class HuiMarkdownCard extends LitElement implements LovelaceCard { class=${classMap({ "no-header": !this._config.title, })} - .content="${this._content}" + .content="${this._templateResult?.result}" > `; @@ -127,7 +130,7 @@ export class HuiMarkdownCard extends LitElement implements LovelaceCard { this._unsubRenderTemplate = subscribeRenderTemplate( this.hass.connection, (result) => { - this._content = result; + this._templateResult = result; }, { template: this._config.content, @@ -139,7 +142,10 @@ export class HuiMarkdownCard extends LitElement implements LovelaceCard { } ); } catch (_err) { - this._content = this._config!.content; + this._templateResult = { + result: this._config!.content, + listeners: { all: false, domains: [], entities: [] }, + }; this._unsubRenderTemplate = undefined; } } diff --git a/src/translations/en.json b/src/translations/en.json index 29cc400560..02cd088e92 100755 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -2729,7 +2729,12 @@ "reset": "Reset to demo template", "jinja_documentation": "Jinja2 template documentation", "template_extensions": "Home Assistant template extensions", - "unknown_error_template": "Unknown error rendering template" + "unknown_error_template": "Unknown error rendering template", + "all_listeners": "This template listens for all state changed events.", + "no_listeners": "This template does not listen for any state changed events and will not update automatically.", + "listeners": "This template listens for the following state changed events:", + "entity": "Entity", + "domain": "Domain" } } }, From dd1bf7b49d2ba6b9aa2635b6070063168e1cd009 Mon Sep 17 00:00:00 2001 From: Ian Richardson Date: Fri, 11 Sep 2020 15:10:36 -0500 Subject: [PATCH 24/41] show first visible view on default (#6567) Co-authored-by: Bram Kragten --- src/panels/lovelace/hui-root.ts | 44 ++++++++++++++++++++++++--------- 1 file changed, 32 insertions(+), 12 deletions(-) diff --git a/src/panels/lovelace/hui-root.ts b/src/panels/lovelace/hui-root.ts index a720fde6bb..56815d7131 100644 --- a/src/panels/lovelace/hui-root.ts +++ b/src/panels/lovelace/hui-root.ts @@ -1,12 +1,13 @@ import "@material/mwc-button"; import "@material/mwc-list/mwc-list-item"; +import type { RequestSelectedDetail } from "@material/mwc-list/mwc-list-item"; import { - mdiDotsVertical, - mdiMicrophone, - mdiPlus, mdiClose, - mdiPencil, + mdiDotsVertical, mdiHelpCircle, + mdiMicrophone, + mdiPencil, + mdiPlus, } from "@mdi/js"; import "@polymer/app-layout/app-header/app-header"; import "@polymer/app-layout/app-scroll-effects/effects/waterfall"; @@ -17,9 +18,9 @@ import { css, CSSResult, html, + internalProperty, LitElement, property, - internalProperty, PropertyValues, TemplateResult, } from "lit-element"; @@ -28,16 +29,17 @@ import memoizeOne from "memoize-one"; import { isComponentLoaded } from "../../common/config/is_component_loaded"; import { fireEvent } from "../../common/dom/fire_event"; import scrollToTarget from "../../common/dom/scroll-to-target"; +import { shouldHandleRequestSelectedEvent } from "../../common/mwc/handle-request-selected-event"; import { navigate } from "../../common/navigate"; import { computeRTLDirection } from "../../common/util/compute_rtl"; import { debounce } from "../../common/util/debounce"; import { afterNextRender } from "../../common/util/render-status"; import "../../components/ha-button-menu"; import "../../components/ha-icon"; -import "../../components/ha-svg-icon"; import "../../components/ha-icon-button-arrow-next"; import "../../components/ha-icon-button-arrow-prev"; import "../../components/ha-menu-button"; +import "../../components/ha-svg-icon"; import type { LovelaceConfig, LovelacePanelConfig, @@ -58,8 +60,6 @@ import type { Lovelace } from "./types"; import "./views/hui-panel-view"; import type { HUIPanelView } from "./views/hui-panel-view"; import { HUIView } from "./views/hui-view"; -import type { RequestSelectedDetail } from "@material/mwc-list/mwc-list-item"; -import { shouldHandleRequestSelectedEvent } from "../../common/mwc/handle-request-selected-event"; class HUIRoot extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; @@ -382,6 +382,15 @@ class HUIRoot extends LitElement { `; } + private _isVisible = (view: LovelaceViewConfig) => + Boolean( + this._editMode || + view.visible === undefined || + view.visible === true || + (Array.isArray(view.visible) && + view.visible.some((show) => show.user === this.hass!.user?.id)) + ); + protected updated(changedProperties: PropertyValues): void { super.updated(changedProperties); @@ -407,9 +416,14 @@ class HUIRoot extends LitElement { if (changedProperties.has("route")) { const views = this.config.views; + if (!viewPath && views.length) { - navigate(this, `${this.route!.prefix}/${views[0].path || 0}`, true); - newSelectView = 0; + newSelectView = views.findIndex(this._isVisible); + navigate( + this, + `${this.route!.prefix}/${views[newSelectView].path || newSelectView}`, + true + ); } else if (viewPath === "hass-unused-entities") { newSelectView = "hass-unused-entities"; } else if (viewPath) { @@ -449,8 +463,14 @@ class HUIRoot extends LitElement { this.lovelace!.mode === "storage" && viewPath === "hass-unused-entities" ) { - navigate(this, `${this.route?.prefix}/${views[0]?.path || 0}`); - newSelectView = 0; + newSelectView = views.findIndex(this._isVisible); + navigate( + this, + `${this.route!.prefix}/${ + views[newSelectView].path || newSelectView + }`, + true + ); } } From da1de8db1d66f08a2acc57cb3b48171f707472bb Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Fri, 11 Sep 2020 22:13:57 +0200 Subject: [PATCH 25/41] Create codeql-analysis.yml --- .github/workflows/codeql-analysis.yml | 60 +++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 .github/workflows/codeql-analysis.yml diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml new file mode 100644 index 0000000000..2dac784cb3 --- /dev/null +++ b/.github/workflows/codeql-analysis.yml @@ -0,0 +1,60 @@ +name: "CodeQL" + +on: + push: + branches: [dev, master] + pull_request: + # The branches below must be a subset of the branches above + branches: [dev] + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + + strategy: + fail-fast: false + matrix: + # Override automatic language detection by changing the below list + # Supported options are ['csharp', 'cpp', 'go', 'java', 'javascript', 'python'] + language: ['javascript', 'python'] + # Learn more... + # https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection + + steps: + - name: Checkout repository + uses: actions/checkout@v2 + with: + # We must fetch at least the immediate parents so that if this is + # a pull request then we can checkout the head. + fetch-depth: 2 + + # If this run was triggered by a pull request event, then checkout + # the head of the pull request instead of the merge commit. + - run: git checkout HEAD^2 + if: ${{ github.event_name == 'pull_request' }} + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v1 + with: + languages: ${{ matrix.language }} + + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v1 + + # ℹ️ Command-line programs to run using the OS shell. + # 📚 https://git.io/JvXDl + + # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines + # and modify them (or add more) to build your code if your project + # uses a compiled language + + #- run: | + # make bootstrap + # make release + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v1 From 3fdd6a80f90d7a339f90bec9b1e6a3e2e8d8cfa9 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Fri, 11 Sep 2020 22:15:08 +0200 Subject: [PATCH 26/41] Update codeql-analysis.yml --- .github/workflows/codeql-analysis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 2dac784cb3..f03f0dcbd0 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -17,7 +17,7 @@ jobs: matrix: # Override automatic language detection by changing the below list # Supported options are ['csharp', 'cpp', 'go', 'java', 'javascript', 'python'] - language: ['javascript', 'python'] + language: ['javascript'] # Learn more... # https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection From f01fe65be40aca9f372ecc224627aca9052e6f83 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Fri, 11 Sep 2020 22:42:11 +0200 Subject: [PATCH 27/41] Show title and name for default panels (#6941) Fixes #6927 --- src/components/ha-sidebar.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/components/ha-sidebar.ts b/src/components/ha-sidebar.ts index 07508cfe3e..73e121c4f9 100644 --- a/src/components/ha-sidebar.ts +++ b/src/components/ha-sidebar.ts @@ -651,11 +651,13 @@ class HaSidebar extends LitElement { return panels.map((panel) => this._renderPanel( panel.url_path, - panel.url_path === "lovelace" - ? this.hass.localize("panel.states") + panel.url_path === this.hass.defaultPanel + ? panel.title || this.hass.localize("panel.states") : this.hass.localize(`panel.${panel.title}`) || panel.title, - panel.url_path === "lovelace" ? undefined : panel.icon, - panel.url_path === "lovelace" ? mdiViewDashboard : undefined + panel.icon, + panel.url_path === this.hass.defaultPanel && !panel.icon + ? mdiViewDashboard + : undefined ) ); } From 26fbc07cacf0a37719119df0f6567e6f48eaaced Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Sat, 12 Sep 2020 00:04:25 +0200 Subject: [PATCH 28/41] Add edit sidebar button to profile (#6943) --- src/components/ha-settings-row.ts | 5 ++++- src/components/ha-sidebar.ts | 20 ++++++++++++++------ src/layouts/home-assistant-main.ts | 12 ++++++++++++ src/panels/profile/ha-panel-profile.ts | 21 +++++++++++++++++++++ src/state/sidebar-mixin.ts | 6 ++---- src/translations/en.json | 8 +++++++- 6 files changed, 60 insertions(+), 12 deletions(-) diff --git a/src/components/ha-settings-row.ts b/src/components/ha-settings-row.ts index 0d53c58d29..4fdae88f3e 100644 --- a/src/components/ha-settings-row.ts +++ b/src/components/ha-settings-row.ts @@ -1,3 +1,4 @@ +import "@polymer/paper-item/paper-item-body"; import { css, CSSResult, @@ -7,7 +8,6 @@ import { property, SVGTemplateResult, } from "lit-element"; -import "@polymer/paper-item/paper-item-body"; @customElement("ha-settings-row") export class HaSettingsRow extends LitElement { @@ -49,6 +49,9 @@ export class HaSettingsRow extends LitElement { border-top: 1px solid var(--divider-color); padding-bottom: 8px; } + ::slotted(ha-switch) { + padding: 16px 0; + } `; } } diff --git a/src/components/ha-sidebar.ts b/src/components/ha-sidebar.ts index 73e121c4f9..49daf43104 100644 --- a/src/components/ha-sidebar.ts +++ b/src/components/ha-sidebar.ts @@ -165,7 +165,7 @@ let sortStyles: CSSResult; class HaSidebar extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; - @property() public narrow!: boolean; + @property({ type: Boolean, reflect: true }) public narrow!: boolean; @property({ type: Boolean }) public alwaysExpand = false; @@ -259,7 +259,7 @@ class HaSidebar extends LitElement {
${this._editMode ? html` - DONE + ${hass.localize("ui.sidebar.done")} ` : "Home Assistant"}
@@ -447,6 +447,9 @@ class HaSidebar extends LitElement { subscribeNotifications(this.hass.connection, (notifications) => { this._notifications = notifications; }); + window.addEventListener("hass-edit-sidebar", () => + this._activateEditMode() + ); } protected updated(changedProps) { @@ -479,11 +482,15 @@ class HaSidebar extends LitElement { return this.shadowRoot!.querySelector(".tooltip")! as HTMLDivElement; } - private async _handleAction(ev: CustomEvent) { + private _handleAction(ev: CustomEvent) { if (ev.detail.action !== "hold") { return; } + this._activateEditMode(); + } + + private async _activateEditMode() { if (!Sortable) { const [sortableImport, sortStylesImport] = await Promise.all([ import("sortablejs/modular/sortable.core.esm"), @@ -498,9 +505,7 @@ class HaSidebar extends LitElement { } this._editMode = true; - if (!this.expanded) { - fireEvent(this, "hass-toggle-menu"); - } + fireEvent(this, "hass-open-menu"); await this.updateComplete; @@ -761,6 +766,9 @@ class HaSidebar extends LitElement { width: 100%; display: none; } + :host([narrow]) .title { + padding: 0 16px; + } :host([expanded]) .title { display: initial; } diff --git a/src/layouts/home-assistant-main.ts b/src/layouts/home-assistant-main.ts index 31609dbdc5..998fb04a3d 100644 --- a/src/layouts/home-assistant-main.ts +++ b/src/layouts/home-assistant-main.ts @@ -24,6 +24,7 @@ const NON_SWIPABLE_PANELS = ["map"]; declare global { // for fire event interface HASSDomEvents { + "hass-open-menu": undefined; "hass-toggle-menu": undefined; "hass-show-notifications": undefined; } @@ -92,6 +93,17 @@ class HomeAssistantMain extends LitElement { protected firstUpdated() { import(/* webpackChunkName: "ha-sidebar" */ "../components/ha-sidebar"); + this.addEventListener("hass-open-menu", () => { + if (this._sidebarNarrow) { + this.drawer.open(); + } else { + fireEvent(this, "hass-dock-sidebar", { + dock: "docked", + }); + setTimeout(() => this.appLayout.resetLayout()); + } + }); + this.addEventListener("hass-toggle-menu", () => { if (this._sidebarNarrow) { if (this.drawer.opened) { diff --git a/src/panels/profile/ha-panel-profile.ts b/src/panels/profile/ha-panel-profile.ts index b4aa386092..458148335c 100644 --- a/src/panels/profile/ha-panel-profile.ts +++ b/src/panels/profile/ha-panel-profile.ts @@ -107,6 +107,23 @@ class HaPanelProfile extends LitElement { .narrow=${this.narrow} .hass=${this.hass} > + + + ${this.hass.localize( + "ui.panel.profile.customize_sidebar.header" + )} + + + ${this.hass.localize( + "ui.panel.profile.customize_sidebar.description" + )} + + + ${this.hass.localize( + "ui.panel.profile.customize_sidebar.button" + )} + + ${this.hass.dockedSidebar !== "auto" || !this.narrow ? html` ; - } - interface HTMLElementEventMap { "hass-default-panel": HASSDomEvent; + "hass-edit-sidebar": undefined; } } diff --git a/src/translations/en.json b/src/translations/en.json index 02cd088e92..e7d2ea366e 100755 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -585,7 +585,8 @@ }, "sidebar": { "external_app_configuration": "App Configuration", - "sidebar_toggle": "Sidebar Toggle" + "sidebar_toggle": "Sidebar Toggle", + "done": "Done" }, "panel": { "calendar": { @@ -2431,6 +2432,11 @@ "header": "Always hide the sidebar", "description": "This will hide the sidebar by default, similar to the mobile experience." }, + "customize_sidebar": { + "header": "Change the order and hide items from the sidebar", + "description": "You can also press and hold the header of the sidebar to activate edit mode.", + "button": "Edit" + }, "vibrate": { "header": "Vibrate", "description": "Enable or disable vibration on this device when controlling devices." From 8ee29b1e43bd29a88392ce11edbb13b50e0d41d3 Mon Sep 17 00:00:00 2001 From: HomeAssistant Azure Date: Sat, 12 Sep 2020 00:32:19 +0000 Subject: [PATCH 29/41] [ci skip] Translation update --- translations/frontend/ar.json | 22 ----- translations/frontend/bs.json | 51 +----------- translations/frontend/ca.json | 4 +- translations/frontend/cs.json | 12 +-- translations/frontend/cy.json | 13 +-- translations/frontend/en.json | 11 +++ translations/frontend/eo.json | 52 +----------- translations/frontend/fa.json | 3 - translations/frontend/gsw.json | 46 +---------- translations/frontend/hi.json | 60 +------------- translations/frontend/hu.json | 82 ++++++++++++++----- translations/frontend/ja.json | 3 - translations/frontend/lt.json | 18 +---- translations/frontend/pl.json | 8 ++ translations/frontend/ru.json | 5 +- translations/frontend/sr-Latn.json | 124 ----------------------------- translations/frontend/sr.json | 120 +--------------------------- translations/frontend/ta.json | 49 +----------- translations/frontend/te.json | 25 +----- translations/frontend/tr.json | 6 +- translations/frontend/ur.json | 52 ------------ translations/frontend/zh-Hans.json | 8 +- 22 files changed, 115 insertions(+), 659 deletions(-) diff --git a/translations/frontend/ar.json b/translations/frontend/ar.json index bb67ab7519..15f0aca0ea 100644 --- a/translations/frontend/ar.json +++ b/translations/frontend/ar.json @@ -1469,34 +1469,12 @@ "next": "التالى", "providers": { "command_line": { - "abort": { - "login_expired": "" - }, - "error": { - "invalid_auth": "" - }, "step": { "init": { "data": { "password": "كلمة السر", "username": "اسم المستخدم" } - }, - "mfa": { - "data": { - "code": "" - }, - "description": "" - } - } - }, - "legacy_api_password": { - "step": { - "mfa": { - "data": { - "code": "" - }, - "description": "" } } }, diff --git a/translations/frontend/bs.json b/translations/frontend/bs.json index e85972507c..f3a2c84456 100644 --- a/translations/frontend/bs.json +++ b/translations/frontend/bs.json @@ -36,8 +36,7 @@ "unknown": "Nepoznat" }, "device_tracker": { - "home": "Kod kuće", - "not_home": "" + "home": "Kod kuće" }, "person": { "home": "Kod kuće" @@ -94,7 +93,6 @@ "on": "Otvoren" }, "presence": { - "off": "", "on": "Kod kuće" }, "problem": { @@ -161,7 +159,6 @@ "closing": "Zatvoreno", "home": "Kod kuće", "locked": "Zaključan", - "not_home": "", "off": "Isključen", "ok": "OK", "on": "Uključen", @@ -245,13 +242,6 @@ "config": { "automation": { "editor": { - "conditions": { - "type": { - "zone": { - "entity": "" - } - } - }, "triggers": { "type": { "mqtt": { @@ -303,45 +293,6 @@ "empty": "Nemate poruke", "playback_title": "Poruku preslušati" }, - "page-authorize": { - "form": { - "providers": { - "command_line": { - "abort": { - "login_expired": "" - }, - "error": { - "invalid_auth": "", - "invalid_code": "" - }, - "step": { - "init": { - "data": { - "password": "", - "username": "" - } - }, - "mfa": { - "data": { - "code": "" - }, - "description": "" - } - } - }, - "legacy_api_password": { - "step": { - "mfa": { - "data": { - "code": "" - }, - "description": "" - } - } - } - } - } - }, "shopping-list": { "add_item": "Dodajte objekat", "clear_completed": "Čišćenje završeno", diff --git a/translations/frontend/ca.json b/translations/frontend/ca.json index 0e0d3b89ec..54945cf396 100644 --- a/translations/frontend/ca.json +++ b/translations/frontend/ca.json @@ -2963,6 +2963,7 @@ "confirm_delete": "Estàs segur que vols eliminar el token d'autenticació d'accés per {name}?", "create": "Crea un token d'autenticació", "create_failed": "No s'ha pogut crear el token d'autenticació d'accés.", + "created": "Creat el {date}", "created_at": "Creat el {date}", "delete_failed": "No s'ha pogut eliminar el token d'autenticació d'accés.", "description": "Crea tokens d'autenticació d'accés de llarga durada per permetre als teus programes/scripts interactuar amb la instància de Home Assistant. Cada token és vàlid durant deu anys després de la seva creació. Els següents tokens d'autenticació d'accés de llarga durada estan actius actualment.", @@ -2970,9 +2971,10 @@ "header": "Tokens d'autenticació d'accés de llarga durada", "last_used": "Darrer ús el {date} des de {location}", "learn_auth_requests": "Aprèn a fer sol·licituds autenticades.", + "name": "Nom", "not_used": "Mai no s'ha utilitzat", "prompt_copy_token": "Copia't el token d'autenticació d'accés. No es tornarà a mostrar més endavant.", - "prompt_name": "Nom?" + "prompt_name": "Posa un nom al token" }, "mfa_setup": { "close": "Tanca", diff --git a/translations/frontend/cs.json b/translations/frontend/cs.json index 17235e792e..8107413439 100644 --- a/translations/frontend/cs.json +++ b/translations/frontend/cs.json @@ -793,7 +793,7 @@ "services": { "reconfigure": "Překonfigurovat zařízení ZHA (opravit zařízení). Použijte, pokud se zařízením máte problémy. Je-li dotyčné zařízení napájené bateriemi, ujistěte se prosím, že je při používání této služby spuštěné a přijímá příkazy.", "remove": "Odebrat zařízení ze sítě Zigbee.", - "updateDeviceName": "Nastavte vlastní název tohoto zařízení v registru zařízení.", + "updateDeviceName": "Nastavte vlastní název tohoto zařízení v seznamu zařízení.", "zigbee_information": "Zobrazit Zigbee informace zařízení." }, "unknown": "Neznámý", @@ -1474,8 +1474,8 @@ "name": "Název", "status": "Stav" }, - "introduction": "Homa Assistant uchovává registr všech entit, které kdy viděl a mohou být jednoznačně identifikovány. Každá z těchto entit bude mít přiděleno ID, které bude rezervováno pouze pro tuto entitu.", - "introduction2": "Pomocí registru entit můžete přepsat název, změnit identifikátor entity nebo odebrat entitu.", + "introduction": "Homa Assistant uchovává záznam o každé entitě, kterou kdy viděl a která může být jednoznačně identifikována. Každá z těchto entit bude mít přiděleno ID, které bude rezervováno pouze pro tuto entitu.", + "introduction2": "Entitě můžete přepsat název, změnit její identifikátor nebo ji odebrat z Home Assistant.", "remove_selected": { "button": "Odstranit vybrané", "confirm_partly_text": "Můžete odebrat pouze {removable} z vybraných {selected} entit. Entity lze odebrat pouze v případě, že integrace již entity neposkytuje. Občas je třeba restartovat Home Assistant před tím, než je možné odstranit entity ze smazané integrace. Opravdu chcete odebrat odstranitelné entity?", @@ -1950,7 +1950,7 @@ }, "server_control": { "caption": "Ovládání serveru", - "description": "Restart a zastavení serveru Home Asistent", + "description": "Restartování a zastavení serveru Home Asistent", "section": { "reloading": { "automation": "Nově načíst automatizace", @@ -2963,6 +2963,7 @@ "confirm_delete": "Opravdu chcete smazat přístupový token pro {name} ?", "create": "Vytvořte token", "create_failed": "Nepodařilo se vytvořit přístupový token.", + "created": "Vytvořeno {date}", "created_at": "Vytvořeno {date}", "delete_failed": "Nepodařilo se odstranit přístupový token.", "description": "Vytvořte přístupové tokeny s dlouhou životností, aby vaše skripty mohly komunikovat s instancí Home Assistant. Každý token bude platný po dobu 10 let od vytvoření. Tyto tokeny s dlouhou životností jsou v současné době aktivní.", @@ -2970,9 +2971,10 @@ "header": "Tokeny s dlouhou životností", "last_used": "Naposledy použito {date} z {location}", "learn_auth_requests": "Naučte se, jak posílat ověřené požadavky.", + "name": "Název", "not_used": "Nikdy nebylo použito", "prompt_copy_token": "Zkopírujte přístupový token. Už nikdy nebude znovu zobrazen.", - "prompt_name": "Název?" + "prompt_name": "Pojmenujte token" }, "mfa_setup": { "close": "Zavřít", diff --git a/translations/frontend/cy.json b/translations/frontend/cy.json index abd54bda52..15f461bf91 100644 --- a/translations/frontend/cy.json +++ b/translations/frontend/cy.json @@ -1283,18 +1283,7 @@ "mfa": { "data": { "code": "Cod dilysu dwy-ffactor" - }, - "description": "" - } - } - }, - "legacy_api_password": { - "step": { - "mfa": { - "data": { - "code": "" - }, - "description": "" + } } } } diff --git a/translations/frontend/en.json b/translations/frontend/en.json index 302930a05a..fcc53ba406 100644 --- a/translations/frontend/en.json +++ b/translations/frontend/en.json @@ -2352,9 +2352,14 @@ "title": "States" }, "templates": { + "all_listeners": "This template listens for all state changed events.", "description": "Templates are rendered using the Jinja2 template engine with some Home Assistant specific extensions.", + "domain": "Domain", "editor": "Template editor", + "entity": "Entity", "jinja_documentation": "Jinja2 template documentation", + "listeners": "This template listens for the following state changed events:", + "no_listeners": "This template does not listen for any state changed events and will not update automatically.", "reset": "Reset to demo template", "template_extensions": "Home Assistant template extensions", "title": "Template", @@ -2941,6 +2946,11 @@ "submit": "Submit" }, "current_user": "You are currently logged in as {fullName}.", + "customize_sidebar": { + "button": "Edit", + "description": "You can also press and hold the header of the sidebar to activate edit mode.", + "header": "Change the order and hide items from the sidebar" + }, "dashboard": { "description": "Pick a default dashboard for this device.", "dropdown_label": "Dashboard", @@ -3038,6 +3048,7 @@ } }, "sidebar": { + "done": "Done", "external_app_configuration": "App Configuration", "sidebar_toggle": "Sidebar Toggle" } diff --git a/translations/frontend/eo.json b/translations/frontend/eo.json index 8b05489328..23fc03e9a9 100644 --- a/translations/frontend/eo.json +++ b/translations/frontend/eo.json @@ -1,49 +1,4 @@ { - "state_badge": { - "device_tracker": { - "home": "" - } - }, - "state": { - "automation": { - "off": "" - }, - "binary_sensor": { - "default": { - "on": "" - }, - "presence": { - "on": "" - } - }, - "calendar": { - "on": "" - }, - "group": { - "home": "", - "off": "", - "on": "" - }, - "input_boolean": { - "on": "" - }, - "light": { - "off": "", - "on": "" - }, - "media_player": { - "off": "" - }, - "script": { - "off": "" - }, - "sensor": { - "off": "" - }, - "switch": { - "on": "" - } - }, "ui": { "card": { "climate": { @@ -124,12 +79,7 @@ "edit_ui": "Redakti kun UI", "edit_yaml": "Redakti kiel YAML", "triggers": { - "name": "Ellasilo", - "type": { - "mqtt": { - "label": "" - } - } + "name": "Ellasilo" } }, "picker": { diff --git a/translations/frontend/fa.json b/translations/frontend/fa.json index 2fc44ddc5d..28992b49cb 100644 --- a/translations/frontend/fa.json +++ b/translations/frontend/fa.json @@ -1689,9 +1689,6 @@ }, "step": { "mfa": { - "data": { - "code": "" - }, "description": "باز کردن **{mfa_module_name}** * * * در دستگاه خود را برای مشاهده شما دو فاکتور تأیید هویت کد و هویت خود را تایید کنید:" } } diff --git a/translations/frontend/gsw.json b/translations/frontend/gsw.json index 2e0d098575..b4e756236a 100644 --- a/translations/frontend/gsw.json +++ b/translations/frontend/gsw.json @@ -37,8 +37,7 @@ "unknown": "Unbekannt" }, "device_tracker": { - "home": "Dahei", - "not_home": "" + "home": "Dahei" }, "person": { "home": "Dahei" @@ -97,7 +96,6 @@ "on": "Offä" }, "presence": { - "off": "", "on": "Dahei" }, "problem": { @@ -419,9 +417,6 @@ "time": { "after": "Nachhär", "before": "Vorhär" - }, - "zone": { - "entity": "" } } }, @@ -515,45 +510,6 @@ "mailbox": { "delete_button": "Lösche" }, - "page-authorize": { - "form": { - "providers": { - "command_line": { - "abort": { - "login_expired": "" - }, - "error": { - "invalid_auth": "", - "invalid_code": "" - }, - "step": { - "init": { - "data": { - "password": "", - "username": "" - } - }, - "mfa": { - "data": { - "code": "" - }, - "description": "" - } - } - }, - "legacy_api_password": { - "step": { - "mfa": { - "data": { - "code": "" - }, - "description": "" - } - } - } - } - } - }, "profile": { "change_password": { "confirm_new_password": "Nöis Passwort bestätige", diff --git a/translations/frontend/hi.json b/translations/frontend/hi.json index 0f06beb774..f71427a683 100644 --- a/translations/frontend/hi.json +++ b/translations/frontend/hi.json @@ -19,9 +19,6 @@ }, "state_badge": { "alarm_control_panel": { - "armed_away": "", - "armed_custom_bypass": "", - "armed_night": "", "pending": "अपूर्ण" }, "default": { @@ -31,8 +28,7 @@ "unknown": "अज्ञात" }, "device_tracker": { - "home": "घर", - "not_home": "" + "home": "घर" }, "person": { "home": "घर" @@ -79,27 +75,16 @@ "off": "विशद", "on": "अनुसन्धानित" }, - "occupancy": { - "off": "", - "on": "" - }, "opening": { "on": "खुला" }, "presence": { - "off": "", "on": "घर" }, "safety": { "off": "सुरक्षित", "on": "असुरक्षित" }, - "smoke": { - "off": "" - }, - "vibration": { - "on": "" - }, "window": { "off": "बंद", "on": "खुली" @@ -130,15 +115,10 @@ "on": "चालू" }, "group": { - "closing": "", "home": "घर", - "not_home": "", "off": "बंद", - "ok": "", "on": "चालू", - "open": "", - "problem": "समस्या", - "stopped": "" + "problem": "समस्या" }, "input_boolean": { "off": "बंद", @@ -254,9 +234,6 @@ "time": { "after": "बाद", "before": "पहले" - }, - "zone": { - "entity": "" } } }, @@ -428,39 +405,6 @@ "form": { "next": "अगला", "providers": { - "command_line": { - "abort": { - "login_expired": "" - }, - "error": { - "invalid_auth": "", - "invalid_code": "" - }, - "step": { - "init": { - "data": { - "password": "", - "username": "" - } - }, - "mfa": { - "data": { - "code": "" - }, - "description": "" - } - } - }, - "legacy_api_password": { - "step": { - "mfa": { - "data": { - "code": "" - }, - "description": "" - } - } - }, "trusted_networks": { "step": { "init": { diff --git a/translations/frontend/hu.json b/translations/frontend/hu.json index 6f4ed3ee3c..0b50127989 100644 --- a/translations/frontend/hu.json +++ b/translations/frontend/hu.json @@ -419,6 +419,8 @@ "unlock": "Kinyit" }, "media_player": { + "media_next_track": "Következő", + "media_previous_track": "Előző", "sound_mode": "Hangzás", "source": "Forrás", "text_to_speak": "Beszéd szövege" @@ -554,6 +556,20 @@ "loading_history": "Állapot előzmények betöltése...", "no_history_found": "Nem található előzmény." }, + "media-browser": { + "choose-source": "Forrás kiválasztása", + "content-type": { + "album": "Album", + "artist": "Előadó", + "library": "Könyvtár", + "playlist": "Lejátszási lista", + "server": "Szerver" + }, + "no_items": "Nincsenek elemek", + "pick-media": "Média kiválasztása", + "play": "Lejátszás", + "play-media": "Média lejátszása" + }, "related-items": { "area": "Terület", "automation": "A következő automatizálások része", @@ -574,6 +590,7 @@ "week": "{count} {count, plural,\n one {héttel}\n other {héttel}\n}" }, "future": "{time} később", + "just_now": "Éppen most", "never": "Soha", "past": "{time} ezelőtt" }, @@ -656,6 +673,9 @@ "required_error_msg": "Ez a mező kötelező", "yaml_not_editable": "Ennek az entitásnak a beállításai nem szerkeszthetők a felhasználói felületről. Csak a felhasználói felületről beállított entitások konfigurálhatók a felhasználói felületről." }, + "image_cropper": { + "crop": "Kivágás" + }, "more_info_control": { "dismiss": "Párbeszédpanel elvetése", "edit": "Entitás szerkesztése", @@ -798,7 +818,7 @@ "confirmation_text": "Minden ebben a területben lévő eszköz hozzárendelés nélküli lesz.", "confirmation_title": "Biztosan törölni szeretnéd ezt a területet?" }, - "description": "Az összes otthoni terület áttekintése", + "description": "Otthoni területek kezelése", "editor": { "area_id": "Terület ID", "create": "Létrehozás", @@ -820,7 +840,7 @@ }, "automation": { "caption": "Automatizálások", - "description": "Automatizálások létrehozása és szerkesztése", + "description": "Automatizálások kezelése", "editor": { "actions": { "add": "Művelet hozzáadása", @@ -852,6 +872,15 @@ "label": "Esemény meghívása", "service_data": "Szolgáltatás adatai" }, + "repeat": { + "label": "Ismétlés", + "type_select": "Ismétlés típusa", + "type": { + "count": { + "label": "Számláló" + } + } + }, "scene": { "label": "Jelenet aktiválása" }, @@ -865,7 +894,7 @@ "wait_template": "Várakozási sablon" } }, - "unsupported_action": "Nem támogatott művelet: {action}" + "unsupported_action": "Nincs felhasználói felület támogatás a művelethez: {action}" }, "alias": "Név", "conditions": { @@ -931,7 +960,7 @@ "zone": "Zóna" } }, - "unsupported_condition": "Nem támogatott feltétel: {condition}" + "unsupported_condition": "Nincs felhasználói felület támogatás a feltételhez: {condition}" }, "default_name": "Új Automatizálás", "description": { @@ -1049,7 +1078,7 @@ "zone": "Zóna" } }, - "unsupported_platform": "Nem támogatott platform: {platform}" + "unsupported_platform": "Nincs felhasználói felület támogatás a platformhoz: {platform}" }, "unsaved_confirm": "Vannak nem mentett módosítások. Biztos, hogy elhagyod az oldalt?" }, @@ -1138,9 +1167,11 @@ }, "alexa": { "banner": "A feltárt entitások szerkesztése funkció le lett tiltva ezen a felületen, mert entitásszűrőket állítottál be a configuration.yaml fájlban.", + "dont_expose_entity": "Entitás feltárásának törlése", "expose": "Feltárás Alexának", + "expose_entity": "Entitás feltárása", "exposed_entities": "Feltárt entitások", - "not_exposed_entities": "Nem feltárt entitások", + "not_exposed_entities": "Feltáratlan entitások", "title": "Alexa" }, "caption": "Home Assistant Felhő", @@ -1177,9 +1208,11 @@ "google": { "banner": "A feltárt entitások szerkesztése funkció le lett tiltva ezen a felületen, mert entitásszűrőket állítottál be a configuration.yaml fájlban.", "disable_2FA": "Kétfaktoros hitelesítés letiltása", + "dont_expose_entity": "Entitás feltárásának törlése", "expose": "Feltárás a Google Asszisztensnek", + "expose_entity": "Entitás feltárása", "exposed_entities": "Feltárt entitások", - "not_exposed_entities": "Nem feltárt entitások", + "not_exposed_entities": "Feltáratlan entitások", "sync_to_google": "A változások szinkronizálása a Google-lal.", "title": "Google Asszisztens" }, @@ -1340,7 +1373,7 @@ }, "entities": { "caption": "Entitások", - "description": "Az összes ismert entitás áttekintése", + "description": "Ismert entitások kezelése", "picker": { "disable_selected": { "button": "Kiválasztottak letiltása", @@ -1392,7 +1425,7 @@ "header": "Home Assistant beállítása", "helpers": { "caption": "Segítők", - "description": "Automatizálások létrehozását segítő elemek", + "description": "Automatizálások létrehozását segítő elemek kezelése", "dialog": { "add_helper": "Segítő hozzáadása", "add_platform": "{platform} hozzáadása", @@ -1420,7 +1453,7 @@ "built_using": "Buildelve:", "caption": "Infó", "custom_uis": "Egyéni felhasználói felületek:", - "description": "Információ a Home Assistant telepítésedről", + "description": "Telepítési információ megtekintése a Home Assistant-ról", "developed_by": "Egy csomó fantasztikus ember által kifejlesztve.", "documentation": "Dokumentáció", "frontend": "frontend-ui", @@ -1459,6 +1492,8 @@ "no_device": "Eszköz nélküli entitások", "no_devices": "Ez az integráció nem rendelkezik eszközökkel.", "options": "Opciók", + "reload": "Újratöltés", + "reload_confirm": "Az integráció újra lett töltve", "rename": "Átnevezés", "restart_confirm": "Indítsd újra a Home Assistant-ot az integráció törlésének befejezéséhez", "settings_button": "{integration} beállításainak szerkesztése", @@ -1483,7 +1518,7 @@ }, "configure": "Beállítás", "configured": "Konfigurálva", - "description": "Integrációk kezelése és beállítása", + "description": "Integrációk kezelése", "details": "Integráció részletei", "discovered": "Felfedezett", "home_assistant_website": "Home Assistant weboldal", @@ -1511,7 +1546,7 @@ "rename_input_label": "Bejegyzés neve", "search": "Integrációk keresése" }, - "introduction": "Itt a komponenseket és a Home Assistant szervert lehet beállítani. Még nem lehet mindent a felületről, de dolgozunk rajta.", + "introduction": "Ebben a nézetben lehetőség van a komponensek és a Home Assistant beállítására. Még nem lehet mindent a felületről, de dolgozunk rajta.", "logs": { "caption": "Napló", "clear": "Törlés", @@ -1567,7 +1602,7 @@ "open": "Megnyitás" } }, - "description": "Lovelace irányítópultjainak konfigurálása", + "description": "Lovelace irányítópultok kezelése", "resources": { "cant_edit_yaml": "A Lovelace-t YAML módban használod, ezért az erőforrásokat nem kezelheted a felhasználói felületről. Kezeld őket a configuration.yaml fájlban.", "caption": "Erőforrások", @@ -1645,7 +1680,7 @@ "scene": { "activated": "Aktivált jelenet: {name}.", "caption": "Jelenetek", - "description": "Jelenetek létrehozása és szerkesztése", + "description": "Jelenetek kezelése", "editor": { "default_name": "Új jelenet", "devices": { @@ -1689,7 +1724,7 @@ }, "script": { "caption": "Szkriptek", - "description": "Szkriptek létrehozása és szerkesztése", + "description": "Szkriptek kezelése", "editor": { "alias": "Név", "default_name": "Új Szkript", @@ -1738,7 +1773,7 @@ "reloading": { "automation": "Automatizálások újratöltése", "core": "Lokáció és testreszabások újratöltése", - "group": "Csoportok újratöltése", + "group": "Csoportok, csoport entitások és értesítési szolgáltatások újratöltése", "heading": "YAML konfiguráció újratöltése", "input_boolean": "Bemeneti logikai változók újratöltése", "input_datetime": "Időpont bemenetek újratöltése", @@ -1768,6 +1803,9 @@ } } }, + "tags": { + "never_scanned": "Sosem volt szkennelve" + }, "users": { "add_user": { "caption": "Felhasználó hozzáadása", @@ -1871,7 +1909,7 @@ "create_group": "Zigbee Home Automation - Csoport létrehozása", "create_group_details": "Add meg a szükséges adatokat egy új zigbee csoport létrehozásához", "creating_group": "Csoport létrehozása", - "description": "Zigbee csoportok létrehozása és módosítása", + "description": "Zigbee csoportok kezelése", "group_details": "Itt van minden részlet a kiválasztott zigbee csoportról.", "group_id": "Csoport ID", "group_info": "Csoportinformációk", @@ -2143,6 +2181,9 @@ "description": "A Gomb kártya gombok hozzáadását teszi lehetővé feladatok végrehajtásához.", "name": "Gomb" }, + "calendar": { + "name": "Naptár" + }, "conditional": { "card": "Kártya", "change_type": "Típus módosítása", @@ -2319,7 +2360,7 @@ "show_code_editor": "Kódszerkesztő megjelenítése", "show_visual_editor": "Vizuális szerkesztő megjelenítése", "toggle_editor": "Szerkesztő", - "typed_header": "{típusú} Kártya Konfiguráció", + "typed_header": "{type} Kártya Konfiguráció", "unsaved_changes": "Vannak nem mentett módosítások" }, "edit_lovelace": { @@ -2396,7 +2437,7 @@ }, "menu": { "close": "Bezárás", - "configure_ui": "Felhasználói felület konfigurálása", + "configure_ui": "Irányítópult szerkesztése", "exit_edit_mode": "Kilépés a felhasználói felület szerkesztési módból", "help": "Súgó", "refresh": "Frissítés", @@ -2589,6 +2630,7 @@ "intro": "Hello {name}, üdvözöl a Home Assistant. Hogyan szeretnéd elnevezni az otthonodat?", "intro_location": "Szeretnénk tudni, hogy hol élsz. Ez segít az információk megjelenítésében és a nap alapú automatizálások beállításában. Ez az adat soha nem lesz megosztva a hálózatodon kívül.", "intro_location_detect": "Segíthetünk neked kitölteni ezt az információt egy külső szolgáltatás egyszeri lekérdezésével.", + "location_name": "A Home Assistant rendszered neve", "location_name_default": "Otthon" }, "integration": { @@ -2659,7 +2701,7 @@ "learn_auth_requests": "Tudj meg többet a hitelesített kérelmek létrehozásáról.", "not_used": "Sosem használt", "prompt_copy_token": "Most másold ki a hozzáférési tokened! Erre később nem lesz lehetőséged.", - "prompt_name": "Név?" + "prompt_name": "Adj nevet a tokennek" }, "mfa_setup": { "close": "Bezárás", diff --git a/translations/frontend/ja.json b/translations/frontend/ja.json index 19aaf17e50..f0d6e46475 100644 --- a/translations/frontend/ja.json +++ b/translations/frontend/ja.json @@ -65,10 +65,7 @@ }, "state_badge": { "alarm_control_panel": { - "armed_away": "", - "armed_custom_bypass": "", "armed_home": "アームしました", - "armed_night": "", "disarmed": "解除", "disarming": "解除", "pending": "保留", diff --git a/translations/frontend/lt.json b/translations/frontend/lt.json index 08fc0028b0..567cd131d2 100644 --- a/translations/frontend/lt.json +++ b/translations/frontend/lt.json @@ -189,7 +189,6 @@ "ok": "Ok", "on": "Įjungta", "open": "Atidarytas", - "problem": "", "stopped": "Sustabdytas", "unlocked": "Atrakinta" }, @@ -409,7 +408,6 @@ "label": "Laikas" }, "zone": { - "entity": "", "label": "Vieta", "zone": "Vieta" } @@ -690,9 +688,6 @@ "form": { "providers": { "command_line": { - "abort": { - "login_expired": "" - }, "error": { "invalid_auth": "Netinkamas vartotojo vardas arba slaptažodis", "invalid_code": "Netinkamas autentifikacijos kodas" @@ -707,18 +702,7 @@ "mfa": { "data": { "code": "Dvieju lygiu autentifikacija" - }, - "description": "" - } - } - }, - "legacy_api_password": { - "step": { - "mfa": { - "data": { - "code": "" - }, - "description": "" + } } } } diff --git a/translations/frontend/pl.json b/translations/frontend/pl.json index cb96ebd163..68d09fa8ae 100644 --- a/translations/frontend/pl.json +++ b/translations/frontend/pl.json @@ -552,6 +552,9 @@ "toggle": "Przełącz" }, "entity": { + "entity-attribute-picker": { + "show_attributes": "Pokaż atrybuty" + }, "entity-picker": { "clear": "Wyczyść", "entity": "Encja", @@ -697,6 +700,7 @@ }, "more_info_control": { "controls": "Sterowanie", + "details": "Szczegóły", "dismiss": "Zamknij okno dialogowe", "edit": "Edytuj encję", "history": "Historia", @@ -1129,6 +1133,8 @@ "add_automation": "Dodaj automatyzację", "delete_automation": "Usuń automatyzację", "delete_confirm": "Czy na pewno chcesz usunąć tę automatyzację?", + "duplicate": "Duplikuj", + "duplicate_automation": "Duplikat automatyzację", "edit_automation": "Edytuj automatyzację", "header": "Edytor automatyzacji", "headers": { @@ -1599,6 +1605,7 @@ "none_found_detail": "Dostosuj kryteria wyszukiwania.", "note_about_integrations": "Jeszcze nie wszystkie integracje można skonfigurować za pomocą interfejsu użytkownika.", "note_about_website_reference": "Więcej jest dostępnych na stronie integracji ", + "reconfigure": "Zmień konfigurację", "rename_dialog": "Edytuj nazwę tego wpisu konfiguracji", "rename_input_label": "Nazwa wpisu", "search": "Szukaj integracji" @@ -2945,6 +2952,7 @@ "header": "Tokeny dostępu", "last_used": "Ostatnio używany {date} z {location}", "learn_auth_requests": "Dowiedz się, jak tworzyć uwierzytelnione żądania.", + "name": "Nazwa", "not_used": "Nigdy nie był używany", "prompt_copy_token": "Skopiuj token. Nie będzie on już ponownie wyświetlany.", "prompt_name": "Nazwa" diff --git a/translations/frontend/ru.json b/translations/frontend/ru.json index 7122ac3008..739b8ccc63 100644 --- a/translations/frontend/ru.json +++ b/translations/frontend/ru.json @@ -706,6 +706,7 @@ }, "more_info_control": { "controls": "Управление", + "details": "Свойства", "dismiss": "Закрыть диалог", "edit": "Изменить объект", "history": "История", @@ -2962,6 +2963,7 @@ "confirm_delete": "Вы уверены, что хотите удалить токен доступа для {name}?", "create": "Создать токен", "create_failed": "Не удалось создать токен доступа.", + "created": "Создан {date}", "created_at": "Создан {date}", "delete_failed": "Не удалось удалить токен доступа.", "description": "Создайте долгосрочные токены доступа, чтобы Ваши скрипты могли взаимодействовать с Home Assistant. Каждый токен будет действителен в течение 10 лет с момента создания. Ниже Вы можете просмотреть долгосрочные токены доступа, которые в настоящее время активны.", @@ -2969,9 +2971,10 @@ "header": "Долгосрочные токены доступа", "last_used": "Последнее использование {date} из {location}", "learn_auth_requests": "Узнайте, как выполнять аутентифицированные запросы.", + "name": "Название", "not_used": "Никогда не использовался", "prompt_copy_token": "Скопируйте Ваш токен доступа. Он больше не будет показан.", - "prompt_name": "Введите название для токена" + "prompt_name": "Название токена" }, "mfa_setup": { "close": "Закрыть", diff --git a/translations/frontend/sr-Latn.json b/translations/frontend/sr-Latn.json index 11f7af24b5..7f497faa5b 100644 --- a/translations/frontend/sr-Latn.json +++ b/translations/frontend/sr-Latn.json @@ -8,85 +8,7 @@ "shopping_list": "Lista za kupovinu", "states": "Pregled" }, - "state_badge": { - "alarm_control_panel": { - "armed_away": "", - "armed_custom_bypass": "", - "armed_night": "" - }, - "device_tracker": { - "home": "", - "not_home": "" - } - }, "state": { - "automation": { - "off": "", - "on": "" - }, - "binary_sensor": { - "default": { - "off": "", - "on": "" - }, - "motion": { - "on": "" - }, - "occupancy": { - "off": "" - }, - "opening": { - "on": "" - }, - "presence": { - "off": "", - "on": "" - }, - "vibration": { - "off": "", - "on": "" - } - }, - "calendar": { - "off": "", - "on": "" - }, - "fan": { - "off": "", - "on": "" - }, - "group": { - "home": "", - "not_home": "", - "off": "", - "on": "", - "problem": "" - }, - "input_boolean": { - "on": "" - }, - "light": { - "off": "", - "on": "" - }, - "media_player": { - "off": "", - "on": "" - }, - "remote": { - "on": "" - }, - "script": { - "off": "", - "on": "" - }, - "sensor": { - "off": "", - "on": "" - }, - "switch": { - "on": "" - }, "weather": { "clear-night": "Vedra noć", "cloudy": "Oblačno", @@ -151,13 +73,6 @@ "config": { "automation": { "editor": { - "conditions": { - "type": { - "zone": { - "entity": "" - } - } - }, "triggers": { "type": { "mqtt": { @@ -192,45 +107,6 @@ "set_config_parameter": "Подесите параметар Цонфиг" } } - }, - "page-authorize": { - "form": { - "providers": { - "command_line": { - "abort": { - "login_expired": "" - }, - "error": { - "invalid_auth": "", - "invalid_code": "" - }, - "step": { - "init": { - "data": { - "password": "", - "username": "" - } - }, - "mfa": { - "data": { - "code": "" - }, - "description": "" - } - } - }, - "legacy_api_password": { - "step": { - "mfa": { - "data": { - "code": "" - }, - "description": "" - } - } - } - } - } } } } diff --git a/translations/frontend/sr.json b/translations/frontend/sr.json index b5debac397..2a32b3aca6 100644 --- a/translations/frontend/sr.json +++ b/translations/frontend/sr.json @@ -11,93 +11,21 @@ }, "state_badge": { "alarm_control_panel": { - "armed_away": "", - "armed_custom_bypass": "", - "armed_night": "", "arming": "Aktiviranje", "disarming": "Deaktiviraj" }, "default": { "entity_not_found": "Вредност није пронађена", "error": "Грешка" - }, - "device_tracker": { - "home": "", - "not_home": "" } }, "state": { - "automation": { - "off": "", - "on": "" - }, - "binary_sensor": { - "default": { - "off": "", - "on": "" - }, - "motion": { - "on": "" - }, - "occupancy": { - "off": "" - }, - "opening": { - "on": "" - }, - "presence": { - "off": "", - "on": "" - }, - "vibration": { - "off": "", - "on": "" - } - }, - "calendar": { - "off": "", - "on": "" - }, - "fan": { - "off": "", - "on": "" - }, - "group": { - "home": "", - "not_home": "", - "off": "", - "on": "", - "problem": "" - }, - "input_boolean": { - "on": "" - }, - "light": { - "off": "", - "on": "" - }, - "media_player": { - "off": "", - "on": "" - }, - "remote": { - "on": "" - }, - "script": { - "off": "", - "on": "" - }, - "sensor": { - "off": "", - "on": "" - }, "sun": { "above_horizon": "Iznad horizonta", "below_horizon": "Ispod horizonta" }, "switch": { - "off": "Isključen", - "on": "" + "off": "Isključen" }, "timer": { "active": "укључен", @@ -128,13 +56,6 @@ "config": { "automation": { "editor": { - "conditions": { - "type": { - "zone": { - "entity": "" - } - } - }, "triggers": { "learn_more": "Сазнајте више о окидачима", "type": { @@ -192,45 +113,6 @@ } } }, - "page-authorize": { - "form": { - "providers": { - "command_line": { - "abort": { - "login_expired": "" - }, - "error": { - "invalid_auth": "", - "invalid_code": "" - }, - "step": { - "init": { - "data": { - "password": "", - "username": "" - } - }, - "mfa": { - "data": { - "code": "" - }, - "description": "" - } - } - }, - "legacy_api_password": { - "step": { - "mfa": { - "data": { - "code": "" - }, - "description": "" - } - } - } - } - } - }, "page-onboarding": { "integration": { "finish": "Крај" diff --git a/translations/frontend/ta.json b/translations/frontend/ta.json index 9043c84c30..7445b2d66a 100644 --- a/translations/frontend/ta.json +++ b/translations/frontend/ta.json @@ -35,8 +35,7 @@ "unknown": "தெரியாத" }, "device_tracker": { - "home": "முகப்பு", - "not_home": "" + "home": "முகப்பு" }, "person": { "home": "முகப்பு" @@ -240,13 +239,6 @@ "config": { "automation": { "editor": { - "conditions": { - "type": { - "zone": { - "entity": "" - } - } - }, "triggers": { "type": { "mqtt": { @@ -265,45 +257,6 @@ "set_config_parameter": "கட்டமைப்பு அளவுருவை அமைக்கவும்" } } - }, - "page-authorize": { - "form": { - "providers": { - "command_line": { - "abort": { - "login_expired": "" - }, - "error": { - "invalid_auth": "", - "invalid_code": "" - }, - "step": { - "init": { - "data": { - "password": "", - "username": "" - } - }, - "mfa": { - "data": { - "code": "" - }, - "description": "" - } - } - }, - "legacy_api_password": { - "step": { - "mfa": { - "data": { - "code": "" - }, - "description": "" - } - } - } - } - } } } } diff --git a/translations/frontend/te.json b/translations/frontend/te.json index 50cf3c0969..1bddd1e80b 100644 --- a/translations/frontend/te.json +++ b/translations/frontend/te.json @@ -37,8 +37,7 @@ "unknown": "తెలియదు" }, "device_tracker": { - "home": "ఇంట", - "not_home": "" + "home": "ఇంట" }, "person": { "home": "ఇంట" @@ -411,9 +410,6 @@ "time": { "after": "తరువాత", "before": "ముందు" - }, - "zone": { - "entity": "" } } }, @@ -535,8 +531,7 @@ "login_expired": "సెషన్ గడువు ముగిసింది, మళ్ళీ లాగిన్ అవ్వండి." }, "error": { - "invalid_auth": "తప్పు యూజర్ పేరు లేదా తప్పు పాస్ వర్డ్", - "invalid_code": "" + "invalid_auth": "తప్పు యూజర్ పేరు లేదా తప్పు పాస్ వర్డ్" }, "step": { "init": { @@ -544,12 +539,6 @@ "password": "పాస్వర్డ్", "username": "యూజర్ పేరు" } - }, - "mfa": { - "data": { - "code": "" - }, - "description": "" } } }, @@ -569,16 +558,6 @@ } } }, - "legacy_api_password": { - "step": { - "mfa": { - "data": { - "code": "" - }, - "description": "" - } - } - }, "trusted_networks": { "abort": { "not_whitelisted": "మీ కంప్యూటర్ అనుమతి జాబితాలో లేదు." diff --git a/translations/frontend/tr.json b/translations/frontend/tr.json index f0e477f212..13376adc80 100644 --- a/translations/frontend/tr.json +++ b/translations/frontend/tr.json @@ -2434,8 +2434,7 @@ "mfa": { "data": { "code": "İki adımlı kimlik doğrulama kodu" - }, - "description": "" + } } } }, @@ -2480,8 +2479,7 @@ "mfa": { "data": { "code": "İki adımlı kimlik doğrulama kodu" - }, - "description": "" + } } } }, diff --git a/translations/frontend/ur.json b/translations/frontend/ur.json index 2f7a214405..c130104798 100644 --- a/translations/frontend/ur.json +++ b/translations/frontend/ur.json @@ -1,49 +1,4 @@ { - "state_badge": { - "device_tracker": { - "home": "" - } - }, - "state": { - "automation": { - "off": "" - }, - "binary_sensor": { - "default": { - "on": "" - }, - "presence": { - "on": "" - } - }, - "calendar": { - "on": "" - }, - "group": { - "home": "", - "off": "", - "on": "" - }, - "input_boolean": { - "on": "" - }, - "light": { - "off": "", - "on": "" - }, - "media_player": { - "off": "" - }, - "script": { - "off": "" - }, - "sensor": { - "off": "" - }, - "switch": { - "on": "" - } - }, "ui": { "card": { "fan": { @@ -84,13 +39,6 @@ "label": "آلہ" } } - }, - "triggers": { - "type": { - "mqtt": { - "label": "" - } - } } } }, diff --git a/translations/frontend/zh-Hans.json b/translations/frontend/zh-Hans.json index fef4a36abf..611909f0cf 100644 --- a/translations/frontend/zh-Hans.json +++ b/translations/frontend/zh-Hans.json @@ -567,6 +567,9 @@ "loading_history": "正在加载历史状态...", "no_history_found": "没有找到历史状态。" }, + "logbook": { + "entries_not_found": "未找到日志条目。" + }, "media-browser": { "audio_not_supported": "您的浏览器不支持音频元素。", "choose_player": "选择播放器", @@ -703,6 +706,7 @@ }, "more_info_control": { "controls": "控制项", + "details": "详情", "dismiss": "关闭对话框", "edit": "编辑实体", "history": "历史", @@ -2959,6 +2963,7 @@ "confirm_delete": "确定要删除 {name} 的访问令牌吗?", "create": "创建令牌", "create_failed": "无法创建访问令牌。", + "created": "创建于 {date}", "created_at": "创建于 {date}", "delete_failed": "无法删除访问令牌。", "description": "创建长期访问令牌以允许脚本与 Home Assistant 实例进行交互。每个令牌在创建后有效期为 10 年。以下是处于激活状态的长期访问令牌。", @@ -2966,9 +2971,10 @@ "header": "长期访问令牌", "last_used": "上次使用于 {date} 来自 {location}", "learn_auth_requests": "了解如何创建经过身份验证的请求。", + "name": "名称", "not_used": "从未使用过", "prompt_copy_token": "请复制您的访问令牌。它不会再显示出来。", - "prompt_name": "名称?" + "prompt_name": "为令牌指定名称" }, "mfa_setup": { "close": "关闭", From c979cfb912a91062e5cb48acfb6cb9137e46f14c Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Sat, 12 Sep 2020 12:52:37 +0200 Subject: [PATCH 30/41] Fix sidebar for not existing hidden panel (#6944) Fixes #6940 --- src/components/ha-sidebar.ts | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/components/ha-sidebar.ts b/src/components/ha-sidebar.ts index 49daf43104..028fe8defd 100644 --- a/src/components/ha-sidebar.ts +++ b/src/components/ha-sidebar.ts @@ -287,27 +287,29 @@ class HaSidebar extends LitElement { ? html` ${this._hiddenPanels.map((url) => { const panel = this.hass.panels[url]; + if (!panel) { + return ""; + } return html` ${panel.url_path === "lovelace" + >${panel.url_path === this.hass.defaultPanel ? hass.localize("panel.states") : hass.localize(`panel.${panel.title}`) || panel.title} - + + + `; })}
@@ -544,7 +546,7 @@ class HaSidebar extends LitElement { private async _unhidePanel(ev: Event) { ev.preventDefault(); - const index = this._hiddenPanels.indexOf((ev.target as any).panel); + const index = this._hiddenPanels.indexOf((ev.currentTarget as any).panel); if (index < 0) { return; } From 266f2e763d98cdb97505a8a13a77329175c17f8a Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Sat, 12 Sep 2020 17:48:40 +0200 Subject: [PATCH 31/41] Sort listening entity and domain in template dev tools (#6953) --- .../template/developer-tools-template.ts | 64 +++++++++++-------- 1 file changed, 36 insertions(+), 28 deletions(-) diff --git a/src/panels/developer-tools/template/developer-tools-template.ts b/src/panels/developer-tools/template/developer-tools-template.ts index f2f011f098..dfabd912d6 100644 --- a/src/panels/developer-tools/template/developer-tools-template.ts +++ b/src/panels/developer-tools/template/developer-tools-template.ts @@ -14,8 +14,8 @@ import { debounce } from "../../../common/util/debounce"; import "../../../components/ha-circular-progress"; import "../../../components/ha-code-editor"; import { - subscribeRenderTemplate, RenderTemplateResult, + subscribeRenderTemplate, } from "../../../data/ws-templates"; import { haStyle } from "../../../resources/styles"; import { HomeAssistant } from "../../../types"; @@ -151,41 +151,49 @@ class HaPanelDevTemplate extends LitElement { ? "" : this._templateResult.listeners.all ? html` - +

${this.hass.localize( "ui.panel.developer-tools.tabs.templates.all_listeners" )} - +

` : this._templateResult.listeners.domains.length || this._templateResult.listeners.entities.length ? html` - ${this.hass.localize( - "ui.panel.developer-tools.tabs.templates.listeners" - )} +

+ ${this.hass.localize( + "ui.panel.developer-tools.tabs.templates.listeners" + )} +

    - ${this._templateResult.listeners.domains.map( - (domain) => - html` -
  • - ${this.hass.localize( - "ui.panel.developer-tools.tabs.templates.domain" - )}: - ${domain} -
  • - ` - )} - ${this._templateResult.listeners.entities.map( - (entity_id) => - html` -
  • - ${this.hass.localize( - "ui.panel.developer-tools.tabs.templates.entity" - )}: - ${entity_id} -
  • - ` - )} + ${this._templateResult.listeners.domains + .sort() + .map( + (domain) => + html` +
  • + ${this.hass.localize( + "ui.panel.developer-tools.tabs.templates.domain" + )}: ${domain} +
  • + ` + )} + ${this._templateResult.listeners.entities + .sort() + .map( + (entity_id) => + html` +
  • + ${this.hass.localize( + "ui.panel.developer-tools.tabs.templates.entity" + )}: ${entity_id} +
  • + ` + )}
` : html` From 90e09fc384d15f029463b9de43a9ff2479354c4f Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Sat, 12 Sep 2020 18:07:14 +0200 Subject: [PATCH 32/41] Add default hold actions (#6952) Fixes #6942 --- src/panels/lovelace/cards/hui-button-card.ts | 30 +++++-------------- src/panels/lovelace/cards/hui-glance-card.ts | 23 ++++++++++---- src/panels/lovelace/cards/hui-light-card.ts | 11 +++---- .../lovelace/cards/hui-picture-glance-card.ts | 10 +++++-- .../lovelace/cards/hui-thermostat-card.ts | 14 ++++----- .../config-elements/hui-light-card-editor.ts | 6 ++-- .../hui-picture-glance-card-editor.ts | 8 ++--- .../lovelace/elements/hui-icon-element.ts | 4 +-- .../lovelace/elements/hui-image-element.ts | 7 +++-- .../elements/hui-state-badge-element.ts | 6 ++-- .../elements/hui-state-icon-element.ts | 10 +++++-- .../elements/hui-state-label-element.ts | 6 ++-- 12 files changed, 69 insertions(+), 66 deletions(-) diff --git a/src/panels/lovelace/cards/hui-button-card.ts b/src/panels/lovelace/cards/hui-button-card.ts index a2662ab7bd..65c045f8ab 100644 --- a/src/panels/lovelace/cards/hui-button-card.ts +++ b/src/panels/lovelace/cards/hui-button-card.ts @@ -21,6 +21,7 @@ import { DOMAINS_TOGGLE } from "../../../common/const"; import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element"; import { computeActiveState } from "../../../common/entity/compute_active_state"; import { computeDomain } from "../../../common/entity/compute_domain"; +import { computeStateDisplay } from "../../../common/entity/compute_state_display"; import { computeStateDomain } from "../../../common/entity/compute_state_domain"; import { computeStateName } from "../../../common/entity/compute_state_name"; import { stateIcon } from "../../../common/entity/state_icon"; @@ -36,7 +37,6 @@ import { hasAction } from "../common/has-action"; import { createEntityNotFoundWarning } from "../components/hui-warning"; import { LovelaceCard, LovelaceCardEditor } from "../types"; import { ButtonCardConfig } from "./types"; -import { computeStateDisplay } from "../../../common/entity/compute_state_display"; @customElement("hui-button-card") export class HuiButtonCard extends LitElement implements LovelaceCard { @@ -63,11 +63,6 @@ export class HuiButtonCard extends LitElement implements LovelaceCard { return { type: "button", - tap_action: { action: "toggle" }, - hold_action: { action: "more-info" }, - show_icon: true, - show_name: true, - show_state: false, entity: foundEntities[0] || "", }; } @@ -92,29 +87,18 @@ export class HuiButtonCard extends LitElement implements LovelaceCard { } this._config = { + tap_action: { + action: + config.entity && DOMAINS_TOGGLE.has(computeDomain(config.entity)) + ? "toggle" + : "more-info", + }, hold_action: { action: "more-info" }, - double_tap_action: { action: "none" }, show_icon: true, show_name: true, state_color: true, ...config, }; - - if (config.entity && DOMAINS_TOGGLE.has(computeDomain(config.entity))) { - this._config = { - tap_action: { - action: "toggle", - }, - ...this._config, - }; - } else { - this._config = { - tap_action: { - action: "more-info", - }, - ...this._config, - }; - } } protected shouldUpdate(changedProps: PropertyValues): boolean { diff --git a/src/panels/lovelace/cards/hui-glance-card.ts b/src/panels/lovelace/cards/hui-glance-card.ts index 46b5be3735..0da08a86a1 100644 --- a/src/panels/lovelace/cards/hui-glance-card.ts +++ b/src/panels/lovelace/cards/hui-glance-card.ts @@ -3,9 +3,9 @@ import { CSSResult, customElement, html, + internalProperty, LitElement, property, - internalProperty, PropertyValues, TemplateResult, } from "lit-element"; @@ -20,18 +20,22 @@ import "../../../components/entity/state-badge"; import "../../../components/ha-card"; import "../../../components/ha-icon"; import { UNAVAILABLE_STATES } from "../../../data/entity"; -import { ActionHandlerEvent } from "../../../data/lovelace"; +import { + ActionHandlerEvent, + CallServiceActionConfig, + MoreInfoActionConfig, +} from "../../../data/lovelace"; import { HomeAssistant } from "../../../types"; import { actionHandler } from "../common/directives/action-handler-directive"; import { findEntities } from "../common/find-entites"; import { handleAction } from "../common/handle-action"; import { hasAction } from "../common/has-action"; import { processConfigEntities } from "../common/process-config-entities"; +import "../components/hui-timestamp-display"; +import { createEntityNotFoundWarning } from "../components/hui-warning"; import "../components/hui-warning-element"; import { LovelaceCard, LovelaceCardEditor } from "../types"; -import "../components/hui-timestamp-display"; import { GlanceCardConfig, GlanceConfigEntity } from "./types"; -import { createEntityNotFoundWarning } from "../components/hui-warning"; @customElement("hui-glance-card") export class HuiGlanceCard extends LitElement implements LovelaceCard { @@ -86,7 +90,14 @@ export class HuiGlanceCard extends LitElement implements LovelaceCard { state_color: true, ...config, }; - const entities = processConfigEntities(config.entities); + const entities = processConfigEntities( + config.entities + ).map((entityConf) => { + return { + hold_action: { action: "more-info" } as MoreInfoActionConfig, + ...entityConf, + }; + }); for (const entity of entities) { if ( @@ -95,7 +106,7 @@ export class HuiGlanceCard extends LitElement implements LovelaceCard { !entity.tap_action.service) || (entity.hold_action && entity.hold_action.action === "call-service" && - !entity.hold_action.service) + !(entity.hold_action as CallServiceActionConfig).service) ) { throw new Error( 'Missing required property "service" when tap_action or hold_action is call-service' diff --git a/src/panels/lovelace/cards/hui-light-card.ts b/src/panels/lovelace/cards/hui-light-card.ts index f05a1c4c1c..0a9bc908b2 100644 --- a/src/panels/lovelace/cards/hui-light-card.ts +++ b/src/panels/lovelace/cards/hui-light-card.ts @@ -1,13 +1,13 @@ -import "../../../components/ha-icon-button"; +import { mdiDotsVertical } from "@mdi/js"; import "@thomasloven/round-slider"; import { css, CSSResult, customElement, html, + internalProperty, LitElement, property, - internalProperty, PropertyValues, TemplateResult, } from "lit-element"; @@ -20,7 +20,8 @@ import { computeStateName } from "../../../common/entity/compute_state_name"; import { stateIcon } from "../../../common/entity/state_icon"; import { supportsFeature } from "../../../common/entity/supports-feature"; import "../../../components/ha-card"; -import { UNAVAILABLE_STATES, UNAVAILABLE } from "../../../data/entity"; +import "../../../components/ha-icon-button"; +import { UNAVAILABLE, UNAVAILABLE_STATES } from "../../../data/entity"; import { SUPPORT_BRIGHTNESS } from "../../../data/light"; import { ActionHandlerEvent } from "../../../data/lovelace"; import { HomeAssistant, LightEntity } from "../../../types"; @@ -32,7 +33,6 @@ import { hasConfigOrEntityChanged } from "../common/has-changed"; import { createEntityNotFoundWarning } from "../components/hui-warning"; import { LovelaceCard, LovelaceCardEditor } from "../types"; import { LightCardConfig } from "./types"; -import { mdiDotsVertical } from "@mdi/js"; @customElement("hui-light-card") export class HuiLightCard extends LitElement implements LovelaceCard { @@ -77,8 +77,9 @@ export class HuiLightCard extends LitElement implements LovelaceCard { } this._config = { - ...config, tap_action: { action: "toggle" }, + hold_action: { action: "more-info" }, + ...config, }; } diff --git a/src/panels/lovelace/cards/hui-picture-glance-card.ts b/src/panels/lovelace/cards/hui-picture-glance-card.ts index 45c0ea1516..0c04eeccf3 100644 --- a/src/panels/lovelace/cards/hui-picture-glance-card.ts +++ b/src/panels/lovelace/cards/hui-picture-glance-card.ts @@ -3,9 +3,9 @@ import { CSSResult, customElement, html, + internalProperty, LitElement, property, - internalProperty, PropertyValues, TemplateResult, } from "lit-element"; @@ -28,10 +28,10 @@ import { hasAction } from "../common/has-action"; import { hasConfigOrEntityChanged } from "../common/has-changed"; import { processConfigEntities } from "../common/process-config-entities"; import "../components/hui-image"; +import { createEntityNotFoundWarning } from "../components/hui-warning"; import "../components/hui-warning-element"; import { LovelaceCard, LovelaceCardEditor } from "../types"; import { PictureGlanceCardConfig, PictureGlanceEntityConfig } from "./types"; -import { createEntityNotFoundWarning } from "../components/hui-warning"; const STATES_OFF = new Set(["closed", "locked", "not_home", "off"]); @@ -104,7 +104,10 @@ class HuiPictureGlanceCard extends LitElement implements LovelaceCard { } }); - this._config = config; + this._config = { + hold_action: { action: "more-info" }, + ...config, + }; } protected shouldUpdate(changedProps: PropertyValues): boolean { @@ -225,6 +228,7 @@ class HuiPictureGlanceCard extends LitElement implements LovelaceCard { entityConf = { tap_action: { action: dialog ? "more-info" : "toggle" }, + hold_action: { action: "more-info" }, ...entityConf, }; diff --git a/src/panels/lovelace/cards/hui-thermostat-card.ts b/src/panels/lovelace/cards/hui-thermostat-card.ts index e8a7f71b48..8470bd4c34 100644 --- a/src/panels/lovelace/cards/hui-thermostat-card.ts +++ b/src/panels/lovelace/cards/hui-thermostat-card.ts @@ -1,4 +1,4 @@ -import "../../../components/ha-icon-button"; +import { mdiDotsVertical } from "@mdi/js"; import "@thomasloven/round-slider"; import { HassEntity } from "home-assistant-js-websocket"; import { @@ -6,13 +6,13 @@ import { CSSResult, customElement, html, + internalProperty, LitElement, property, - internalProperty, PropertyValues, + query, svg, TemplateResult, - query, } from "lit-element"; import { classMap } from "lit-html/directives/class-map"; import { UNIT_F } from "../../../common/const"; @@ -20,6 +20,8 @@ import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_elemen import { fireEvent } from "../../../common/dom/fire_event"; import { computeStateName } from "../../../common/entity/compute_state_name"; import "../../../components/ha-card"; +import type { HaCard } from "../../../components/ha-card"; +import "../../../components/ha-icon-button"; import { ClimateEntity, CLIMATE_PRESET_NONE, @@ -28,14 +30,11 @@ import { } from "../../../data/climate"; import { UNAVAILABLE } from "../../../data/entity"; import { HomeAssistant } from "../../../types"; -import { actionHandler } from "../common/directives/action-handler-directive"; import { findEntities } from "../common/find-entites"; import { hasConfigOrEntityChanged } from "../common/has-changed"; import { createEntityNotFoundWarning } from "../components/hui-warning"; import { LovelaceCard, LovelaceCardEditor } from "../types"; import { ThermostatCardConfig } from "./types"; -import type { HaCard } from "../../../components/ha-card"; -import { mdiDotsVertical } from "@mdi/js"; const modeIcons: { [mode in HvacMode]: string } = { auto: "hass:calendar-sync", @@ -385,8 +384,7 @@ export class HuiThermostatCard extends LitElement implements LovelaceCard { class="${classMap({ "selected-icon": currentMode === mode })}" .mode="${mode}" .icon="${modeIcons[mode]}" - @action=${this._handleAction} - .actionHandler=${actionHandler()} + @click=${this._handleAction} tabindex="0" > `; diff --git a/src/panels/lovelace/editor/config-elements/hui-light-card-editor.ts b/src/panels/lovelace/editor/config-elements/hui-light-card-editor.ts index 90b63d4276..1d90cd20ad 100644 --- a/src/panels/lovelace/editor/config-elements/hui-light-card-editor.ts +++ b/src/panels/lovelace/editor/config-elements/hui-light-card-editor.ts @@ -62,11 +62,11 @@ export class HuiLightCardEditor extends LitElement } get _hold_action(): ActionConfig { - return this._config!.hold_action || { action: "none" }; + return this._config!.hold_action || { action: "more-info" }; } - get _double_tap_action(): ActionConfig { - return this._config!.double_tap_action || { action: "none" }; + get _double_tap_action(): ActionConfig | undefined { + return this._config!.double_tap_action; } protected render(): TemplateResult { diff --git a/src/panels/lovelace/editor/config-elements/hui-picture-glance-card-editor.ts b/src/panels/lovelace/editor/config-elements/hui-picture-glance-card-editor.ts index 7f8ef8ebd9..0009348efb 100644 --- a/src/panels/lovelace/editor/config-elements/hui-picture-glance-card-editor.ts +++ b/src/panels/lovelace/editor/config-elements/hui-picture-glance-card-editor.ts @@ -90,12 +90,12 @@ export class HuiPictureGlanceCardEditor extends LitElement return this._config!.aspect_ratio || ""; } - get _tap_action(): ActionConfig | undefined { - return this._config!.tap_action; + get _tap_action(): ActionConfig { + return this._config!.tap_action || { action: "toggle" }; } - get _hold_action(): ActionConfig | undefined { - return this._config!.hold_action; + get _hold_action(): ActionConfig { + return this._config!.hold_action || { action: "more-info" }; } get _theme(): string { diff --git a/src/panels/lovelace/elements/hui-icon-element.ts b/src/panels/lovelace/elements/hui-icon-element.ts index 82e7a7fc39..4811f219ac 100644 --- a/src/panels/lovelace/elements/hui-icon-element.ts +++ b/src/panels/lovelace/elements/hui-icon-element.ts @@ -3,8 +3,8 @@ import { CSSResult, customElement, html, - LitElement, internalProperty, + LitElement, TemplateResult, } from "lit-element"; import { ifDefined } from "lit-html/directives/if-defined"; @@ -28,7 +28,7 @@ export class HuiIconElement extends LitElement implements LovelaceElement { throw Error("Invalid Configuration: 'icon' required"); } - this._config = config; + this._config = { hold_action: { action: "more-info" }, ...config }; } protected render(): TemplateResult { diff --git a/src/panels/lovelace/elements/hui-image-element.ts b/src/panels/lovelace/elements/hui-image-element.ts index a42c7bab8f..50e45c153a 100644 --- a/src/panels/lovelace/elements/hui-image-element.ts +++ b/src/panels/lovelace/elements/hui-image-element.ts @@ -3,9 +3,9 @@ import { CSSResult, customElement, html, + internalProperty, LitElement, property, - internalProperty, TemplateResult, } from "lit-element"; import { ifDefined } from "lit-html/directives/if-defined"; @@ -29,12 +29,13 @@ export class HuiImageElement extends LitElement implements LovelaceElement { throw Error("Error in element configuration"); } + this._config = { hold_action: { action: "more-info" }, ...config }; + // eslint-disable-next-line wc/no-self-class this.classList.toggle( "clickable", - config.tap_action && config.tap_action.action !== "none" + this._config.tap_action && this._config.tap_action.action !== "none" ); - this._config = config; } protected render(): TemplateResult { diff --git a/src/panels/lovelace/elements/hui-state-badge-element.ts b/src/panels/lovelace/elements/hui-state-badge-element.ts index 2a008921fa..1baa6b0d8d 100644 --- a/src/panels/lovelace/elements/hui-state-badge-element.ts +++ b/src/panels/lovelace/elements/hui-state-badge-element.ts @@ -1,9 +1,9 @@ import { customElement, html, + internalProperty, LitElement, property, - internalProperty, PropertyValues, TemplateResult, } from "lit-element"; @@ -16,9 +16,9 @@ import { actionHandler } from "../common/directives/action-handler-directive"; import { handleAction } from "../common/handle-action"; import { hasAction } from "../common/has-action"; import { hasConfigOrEntityChanged } from "../common/has-changed"; +import { createEntityNotFoundWarning } from "../components/hui-warning"; import "../components/hui-warning-element"; import { LovelaceElement, StateBadgeElementConfig } from "./types"; -import { createEntityNotFoundWarning } from "../components/hui-warning"; @customElement("hui-state-badge-element") export class HuiStateBadgeElement extends LitElement @@ -32,7 +32,7 @@ export class HuiStateBadgeElement extends LitElement throw Error("Invalid Configuration: 'entity' required"); } - this._config = config; + this._config = { hold_action: { action: "more-info" }, ...config }; } protected shouldUpdate(changedProps: PropertyValues): boolean { diff --git a/src/panels/lovelace/elements/hui-state-icon-element.ts b/src/panels/lovelace/elements/hui-state-icon-element.ts index d4a1a2eba1..84c8593ad0 100644 --- a/src/panels/lovelace/elements/hui-state-icon-element.ts +++ b/src/panels/lovelace/elements/hui-state-icon-element.ts @@ -3,9 +3,9 @@ import { CSSResult, customElement, html, + internalProperty, LitElement, property, - internalProperty, PropertyValues, TemplateResult, } from "lit-element"; @@ -18,9 +18,9 @@ import { actionHandler } from "../common/directives/action-handler-directive"; import { handleAction } from "../common/handle-action"; import { hasAction } from "../common/has-action"; import { hasConfigOrEntityChanged } from "../common/has-changed"; +import { createEntityNotFoundWarning } from "../components/hui-warning"; import "../components/hui-warning-element"; import { LovelaceElement, StateIconElementConfig } from "./types"; -import { createEntityNotFoundWarning } from "../components/hui-warning"; @customElement("hui-state-icon-element") export class HuiStateIconElement extends LitElement implements LovelaceElement { @@ -33,7 +33,11 @@ export class HuiStateIconElement extends LitElement implements LovelaceElement { throw Error("Invalid Configuration: 'entity' required"); } - this._config = { state_color: true, ...config }; + this._config = { + state_color: true, + hold_action: { action: "more-info" }, + ...config, + }; } protected shouldUpdate(changedProps: PropertyValues): boolean { diff --git a/src/panels/lovelace/elements/hui-state-label-element.ts b/src/panels/lovelace/elements/hui-state-label-element.ts index 08de7d4516..7296f0dc64 100644 --- a/src/panels/lovelace/elements/hui-state-label-element.ts +++ b/src/panels/lovelace/elements/hui-state-label-element.ts @@ -3,9 +3,9 @@ import { CSSResult, customElement, html, + internalProperty, LitElement, property, - internalProperty, PropertyValues, TemplateResult, } from "lit-element"; @@ -18,9 +18,9 @@ import { actionHandler } from "../common/directives/action-handler-directive"; import { handleAction } from "../common/handle-action"; import { hasAction } from "../common/has-action"; import { hasConfigOrEntityChanged } from "../common/has-changed"; +import { createEntityNotFoundWarning } from "../components/hui-warning"; import "../components/hui-warning-element"; import { LovelaceElement, StateLabelElementConfig } from "./types"; -import { createEntityNotFoundWarning } from "../components/hui-warning"; @customElement("hui-state-label-element") class HuiStateLabelElement extends LitElement implements LovelaceElement { @@ -33,7 +33,7 @@ class HuiStateLabelElement extends LitElement implements LovelaceElement { throw Error("Invalid Configuration: 'entity' required"); } - this._config = config; + this._config = { hold_action: { action: "more-info" }, ...config }; } protected shouldUpdate(changedProps: PropertyValues): boolean { From 7e70ba6ab2299b7e148b36746bde48155f2139ac Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Sat, 12 Sep 2020 18:18:57 +0200 Subject: [PATCH 33/41] FIx entities picker (#6951) Fixes #6947 ? --- src/components/entity/ha-entity-picker.ts | 6 ++-- .../lovelace/components/hui-entity-editor.ts | 31 ++++++++++--------- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/src/components/entity/ha-entity-picker.ts b/src/components/entity/ha-entity-picker.ts index 51ddf0d30f..3cafa0a88b 100644 --- a/src/components/entity/ha-entity-picker.ts +++ b/src/components/entity/ha-entity-picker.ts @@ -6,6 +6,7 @@ import { HassEntity } from "home-assistant-js-websocket"; import { css, CSSResult, + customElement, html, LitElement, property, @@ -51,7 +52,8 @@ const rowRenderer = ( root.querySelector("[secondary]")!.textContent = model.item.entity_id; }; -class HaEntityPicker extends LitElement { +@customElement("ha-entity-picker") +export class HaEntityPicker extends LitElement { @property({ type: Boolean }) public autofocus = false; @property({ type: Boolean }) public disabled?: boolean; @@ -276,8 +278,6 @@ class HaEntityPicker extends LitElement { } } -customElements.define("ha-entity-picker", HaEntityPicker); - declare global { interface HTMLElementTagNameMap { "ha-entity-picker": HaEntityPicker; diff --git a/src/panels/lovelace/components/hui-entity-editor.ts b/src/panels/lovelace/components/hui-entity-editor.ts index 90e4b5eeb4..ddde4ef768 100644 --- a/src/panels/lovelace/components/hui-entity-editor.ts +++ b/src/panels/lovelace/components/hui-entity-editor.ts @@ -18,10 +18,10 @@ import Sortable, { } from "sortablejs/modular/sortable.core.esm"; import { fireEvent } from "../../../common/dom/fire_event"; import "../../../components/entity/ha-entity-picker"; +import type { HaEntityPicker } from "../../../components/entity/ha-entity-picker"; import "../../../components/ha-icon-button"; import { sortableStyles } from "../../../resources/ha-sortable-style"; import { HomeAssistant } from "../../../types"; -import { EditorTarget } from "../editor/types"; import { EntityConfig } from "../entity-rows/types"; @customElement("hui-entity-editor") @@ -73,7 +73,7 @@ export class HuiEntityEditor extends LitElement { .hass=${this.hass} .value=${entityConf.entity} .index=${index} - @change=${this._valueChanged} + @value-changed=${this._valueChanged} allow-custom-entity > @@ -83,7 +83,7 @@ export class HuiEntityEditor extends LitElement { `; } @@ -136,15 +136,15 @@ export class HuiEntityEditor extends LitElement { }); } - private async _addEntity(ev: Event): Promise { - const target = ev.target! as EditorTarget; - if (target.value === "") { + private async _addEntity(ev: CustomEvent): Promise { + const value = ev.detail.value; + if (value === "") { return; } const newConfigEntities = this.entities!.concat({ - entity: target.value as string, + entity: value as string, }); - target.value = ""; + (ev.target as HaEntityPicker).value = ""; fireEvent(this, "entities-changed", { entities: newConfigEntities }); } @@ -160,16 +160,17 @@ export class HuiEntityEditor extends LitElement { fireEvent(this, "entities-changed", { entities: newEntities }); } - private _valueChanged(ev: Event): void { - const target = ev.target! as EditorTarget; + private _valueChanged(ev: CustomEvent): void { + const value = ev.detail.value; + const index = (ev.target as any).index; const newConfigEntities = this.entities!.concat(); - if (target.value === "") { - newConfigEntities.splice(target.index!, 1); + if (value === "") { + newConfigEntities.splice(index, 1); } else { - newConfigEntities[target.index!] = { - ...newConfigEntities[target.index!], - entity: target.value!, + newConfigEntities[index] = { + ...newConfigEntities[index], + entity: value!, }; } From 8b490c50475cca775cc7e8d6bcb4f35b41ef5f69 Mon Sep 17 00:00:00 2001 From: Zack Barett Date: Sat, 12 Sep 2020 11:59:19 -0500 Subject: [PATCH 34/41] Media Browser: Use Media Class (#6904) Co-authored-by: Bram Kragten --- .../media-player/ha-media-player-browse.ts | 334 ++++++++++-------- src/data/media-player.ts | 80 +++++ .../media-browser/ha-panel-media-browser.ts | 39 +- src/translations/en.json | 25 +- 4 files changed, 313 insertions(+), 165 deletions(-) diff --git a/src/components/media-player/ha-media-player-browse.ts b/src/components/media-player/ha-media-player-browse.ts index 741a85b238..7bf868254f 100644 --- a/src/components/media-player/ha-media-player-browse.ts +++ b/src/components/media-player/ha-media-player-browse.ts @@ -2,7 +2,7 @@ import "@material/mwc-button/mwc-button"; import "@material/mwc-fab/mwc-fab"; import "@material/mwc-list/mwc-list"; import "@material/mwc-list/mwc-list-item"; -import { mdiArrowLeft, mdiClose, mdiFolder, mdiPlay, mdiPlus } from "@mdi/js"; +import { mdiArrowLeft, mdiClose, mdiPlay, mdiPlus } from "@mdi/js"; import "@polymer/paper-item/paper-item"; import "@polymer/paper-listbox/paper-listbox"; import { @@ -19,7 +19,6 @@ import { import { classMap } from "lit-html/directives/class-map"; import { ifDefined } from "lit-html/directives/if-defined"; import { styleMap } from "lit-html/directives/style-map"; -import memoizeOne from "memoize-one"; import { fireEvent } from "../../common/dom/fire_event"; import { computeRTLDirection } from "../../common/util/compute_rtl"; import { debounce } from "../../common/util/debounce"; @@ -27,6 +26,7 @@ import { browseLocalMediaPlayer, browseMediaPlayer, BROWSER_SOURCE, + MediaClassBrowserSettings, MediaPickedEvent, MediaPlayerBrowseAction, } from "../../data/media-player"; @@ -93,34 +93,6 @@ export class HaMediaPlayerBrowse extends LitElement { this._navigate(item); } - private _renderError(err: { message: string; code: string }) { - if (err.message === "Media directory does not exist.") { - return html` -

No local media found.

-

- It looks like you have not yet created a media directory. -
Create a directory with the name "media" in the - configuration directory of Home Assistant - (${this.hass.config.config_dir}).
Place your video, audio and - image files in this directory to be able to browse and play them in - the browser or on supported media players. -

- -

- Check the - documentation - for more info -

- `; - } - return err.message; - } - protected render(): TemplateResult { if (this._loading) { return html``; @@ -136,7 +108,7 @@ export class HaMediaPlayerBrowse extends LitElement { text: this._renderError(this._error), }); } else { - return html`
+ return html`
${this._renderError(this._error)}
`; } @@ -155,17 +127,12 @@ export class HaMediaPlayerBrowse extends LitElement { ? this._mediaPlayerItems[this._mediaPlayerItems.length - 2] : undefined; - const hasExpandableChildren: - | MediaPlayerItem - | undefined = this._hasExpandableChildren(currentItem.children); - - const showImages: boolean | undefined = currentItem.children?.some( - (child) => child.thumbnail && child.thumbnail !== currentItem.thumbnail - ); - - const mediaType = this.hass.localize( - `ui.components.media-browser.content-type.${currentItem.media_content_type}` + const subtitle = this.hass.localize( + `ui.components.media-browser.class.${currentItem.media_class}` ); + const mediaClass = MediaClassBrowserSettings[currentItem.media_class]; + const childrenMediaClass = + MediaClassBrowserSettings[currentItem.children_media_class]; return html`
-
- ${currentItem.thumbnail - ? html` -
- ${this._narrow && currentItem?.can_play - ? html` - - +
+ ${currentItem.thumbnail + ? html` +
+ ${this._narrow && currentItem?.can_play + ? html` + + + ${this.hass.localize( + `ui.components.media-browser.${this.action}` )} - .path=${this.action === "play" ? mdiPlay : mdiPlus} - > - ${this.hass.localize( - `ui.components.media-browser.${this.action}` - )} - - ` - : ""} -
- ` - : html``} -
- + ` + : html``} +
+ + ${currentItem.can_play && + (!currentItem.thumbnail || !this._narrow) ? html` -
- - ${previousItem.title} -
- ` - : ""} -

${currentItem.title}

- ${mediaType - ? html` -

- ${mediaType} -

+ + + ${this.hass.localize( + `ui.components.media-browser.${this.action}` + )} + ` : ""}
- ${currentItem.can_play && (!currentItem.thumbnail || !this._narrow) - ? html` - - - ${this.hass.localize( - `ui.components.media-browser.${this.action}` - )} - - ` - : ""}
+ ${this.dialog + ? html` + + + + ` + : ""}
- ${this.dialog - ? html` - - - - ` - : ""}
${this._error - ? html`
- ${this._renderError(this._error)} -
` + ? html` +
+ ${this._renderError(this._error)} +
+ ` : currentItem.children?.length - ? hasExpandableChildren + ? childrenMediaClass.layout === "grid" ? html` -
+
${currentItem.children.map( (child) => html`
- ${child.can_expand && !child.thumbnail + ${!child.thumbnail ? html` ` : ""} @@ -298,7 +281,9 @@ export class HaMediaPlayerBrowse extends LitElement { ${child.can_play ? html` html` ` - : html`
- ${this.hass.localize("ui.components.media-browser.no_items")} -
`} + : html` +
+ ${this.hass.localize("ui.components.media-browser.no_items")} +
+ `} `; } @@ -504,14 +492,38 @@ export class HaMediaPlayerBrowse extends LitElement { this._resizeObserver.observe(this); } - private _hasExpandableChildren = memoizeOne((children?: MediaPlayerItem[]) => - children?.find((item: MediaPlayerItem) => item.can_expand) - ); - private _closeDialogAction(): void { fireEvent(this, "close-dialog"); } + private _renderError(err: { message: string; code: string }) { + if (err.message === "Media directory does not exist.") { + return html` +

No local media found.

+

+ It looks like you have not yet created a media directory. +
Create a directory with the name "media" in the + configuration directory of Home Assistant + (${this.hass.config.config_dir}).
Place your video, audio and + image files in this directory to be able to browse and play them in + the browser or on supported media players. +

+ +

+ Check the + documentation + for more info +

+ `; + } + return html`err.message`; + } + static get styles(): CSSResultArray { return [ haStyle, @@ -529,12 +541,9 @@ export class HaMediaPlayerBrowse extends LitElement { } .header { - display: flex; + display: block; justify-content: space-between; border-bottom: 1px solid var(--divider-color); - } - - .header { background-color: var(--card-background-color); position: sticky; position: -webkit-sticky; @@ -543,6 +552,10 @@ export class HaMediaPlayerBrowse extends LitElement { padding: 20px 24px 10px; } + .header-wrapper { + display: flex; + } + .header-content { display: flex; flex-wrap: wrap; @@ -570,6 +583,7 @@ export class HaMediaPlayerBrowse extends LitElement { .header-info mwc-button { display: block; + --mdc-theme-primary: var(--primary-color); } .breadcrumb { @@ -655,7 +669,7 @@ export class HaMediaPlayerBrowse extends LitElement { width: 100%; } - ha-card { + .children ha-card { width: 100%; padding-bottom: 100%; position: relative; @@ -663,6 +677,11 @@ export class HaMediaPlayerBrowse extends LitElement { background-size: cover; background-repeat: no-repeat; background-position: center; + transition: padding-bottom 0.1s ease-out; + } + + .portrait.children ha-card { + padding-bottom: 150%; } .child .folder, @@ -678,18 +697,36 @@ export class HaMediaPlayerBrowse extends LitElement { } .child .play { + transition: color 0.5s; + border-radius: 50%; + bottom: calc(50% - 35px); + right: calc(50% - 35px); + opacity: 0; + transition: opacity 0.1s ease-out; + } + + .child .play:not(.can_expand) { + --mdc-icon-button-size: 70px; + --mdc-icon-size: 48px; + } + + .ha-card-parent:hover .play:not(.can_expand) { + opacity: 1; + color: var(--primary-color); + } + + .child .play.can_expand { + opacity: 1; + background-color: rgba(var(--rgb-card-background-color), 0.5); bottom: 4px; right: 4px; - transition: all 0.5s; - background-color: rgba(var(--rgb-card-background-color), 0.5); - border-radius: 50%; } .child .play:hover { color: var(--primary-color); } - ha-card:hover { + .ha-card-parent:hover ha-card { opacity: 0.5; } @@ -706,6 +743,7 @@ export class HaMediaPlayerBrowse extends LitElement { .child .type { font-size: 12px; color: var(--secondary-text-color); + padding-left: 2px; } mwc-list-item .graphic { diff --git a/src/data/media-player.ts b/src/data/media-player.ts index af11824211..70b29c2d63 100644 --- a/src/data/media-player.ts +++ b/src/data/media-player.ts @@ -1,5 +1,23 @@ import type { HassEntity } from "home-assistant-js-websocket"; import type { HomeAssistant } from "../types"; +import { + mdiFolder, + mdiPlaylistMusic, + mdiFileMusic, + mdiAlbum, + mdiMusic, + mdiTelevisionClassic, + mdiMovie, + mdiVideo, + mdiImage, + mdiWeb, + mdiGamepadVariant, + mdiAccountMusic, + mdiPodcast, + mdiApplication, + mdiAccountMusicOutline, + mdiDramaMasks, +} from "@mdi/js"; export const SUPPORT_PAUSE = 1; export const SUPPORT_SEEK = 2; @@ -22,6 +40,66 @@ export type MediaPlayerBrowseAction = "pick" | "play"; export const BROWSER_SOURCE = "browser"; +export type MediaClassBrowserSetting = { + icon: string; + thumbnail_ratio?: string; + layout?: string; + show_list_images?: boolean; +}; + +export const MediaClassBrowserSettings: { + [type: string]: MediaClassBrowserSetting; +} = { + album: { icon: mdiAlbum, layout: "grid" }, + app: { icon: mdiApplication, layout: "grid" }, + artist: { icon: mdiAccountMusic, layout: "grid", show_list_images: true }, + channel: { + icon: mdiTelevisionClassic, + thumbnail_ratio: "portrait", + layout: "grid", + }, + composer: { + icon: mdiAccountMusicOutline, + layout: "grid", + show_list_images: true, + }, + contributing_artist: { + icon: mdiAccountMusic, + layout: "grid", + show_list_images: true, + }, + directory: { icon: mdiFolder, layout: "grid", show_list_images: true }, + episode: { + icon: mdiTelevisionClassic, + layout: "grid", + thumbnail_ratio: "portrait", + }, + game: { + icon: mdiGamepadVariant, + layout: "grid", + thumbnail_ratio: "portrait", + }, + genre: { icon: mdiDramaMasks, layout: "grid", show_list_images: true }, + image: { icon: mdiImage, layout: "grid" }, + movie: { icon: mdiMovie, thumbnail_ratio: "portrait", layout: "grid" }, + music: { icon: mdiMusic }, + playlist: { icon: mdiPlaylistMusic, layout: "grid", show_list_images: true }, + podcast: { icon: mdiPodcast, layout: "grid" }, + season: { + icon: mdiTelevisionClassic, + layout: "grid", + thumbnail_ratio: "portrait", + }, + track: { icon: mdiFileMusic }, + tv_show: { + icon: mdiTelevisionClassic, + layout: "grid", + thumbnail_ratio: "portrait", + }, + url: { icon: mdiWeb }, + video: { icon: mdiVideo, layout: "grid" }, +}; + export interface MediaPickedEvent { item: MediaPlayerItem; } @@ -40,6 +118,8 @@ export interface MediaPlayerItem { title: string; media_content_type: string; media_content_id: string; + media_class: string; + children_media_class: string; can_play: boolean; can_expand: boolean; thumbnail?: string; diff --git a/src/panels/media-browser/ha-panel-media-browser.ts b/src/panels/media-browser/ha-panel-media-browser.ts index a58243857e..63c71d2e92 100644 --- a/src/panels/media-browser/ha-panel-media-browser.ts +++ b/src/panels/media-browser/ha-panel-media-browser.ts @@ -1,5 +1,4 @@ import "@material/mwc-icon-button"; -import { mdiPlayNetwork } from "@mdi/js"; import "@polymer/app-layout/app-header/app-header"; import "@polymer/app-layout/app-toolbar/app-toolbar"; import { @@ -46,9 +45,9 @@ class PanelMediaBrowser extends LitElement { const title = this._entityId === BROWSER_SOURCE - ? `${this.hass.localize("ui.components.media-browser.web-browser")} - ` + ? `${this.hass.localize("ui.components.media-browser.web-browser")}` : stateObj?.attributes.friendly_name - ? `${stateObj?.attributes.friendly_name} - ` + ? `${stateObj?.attributes.friendly_name}` : undefined; return html` @@ -59,17 +58,17 @@ class PanelMediaBrowser extends LitElement { .hass=${this.hass} .narrow=${this.narrow} > -
- ${title || ""}${this.hass.localize( - "ui.components.media-browser.media-player-browser" - )} -
- - +
+
${this.hass.localize( - "ui.components.media-browser.choose_player" + "ui.components.media-browser.media-player-browser" )} - +
+
${title || ""}
+
+ + ${this.hass.localize("ui.components.media-browser.choose_player")} +
@@ -134,9 +133,25 @@ class PanelMediaBrowser extends LitElement { return [ haStyle, css` + :host { + --mdc-theme-primary: var(--app-header-text-color); + } ha-media-player-browse { height: calc(100vh - 84px); } + :host([narrow]) app-toolbar mwc-button { + width: 65px; + } + .heading { + overflow: hidden; + white-space: nowrap; + } + .heading .secondary { + color: var(--secondary-text-color); + font-size: 14px; + overflow: hidden; + text-overflow: ellipsis; + } `, ]; } diff --git a/src/translations/en.json b/src/translations/en.json index e7d2ea366e..8eadb910a9 100755 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -373,12 +373,27 @@ "video_not_supported": "Your browser does not support the video element.", "media_not_supported": "The Browser Media Player does not support this type of media", "media_browsing_error": "Media Browsing Error", - "content-type": { - "server": "Server", - "library": "Library", - "artist": "Artist", + "class": { "album": "Album", - "playlist": "Playlist" + "app": "App", + "artist": "Artist", + "channel": "Channel", + "composer": "Composer", + "contributing_artist": "Contributing Artist", + "directory": "Library", + "episode": "Episode", + "game": "Game", + "genre": "Genre", + "image": "Image", + "movie": "Movie", + "music": "Music", + "playlist": "Playlist", + "podcast": "Podcast", + "season": "Season", + "track": "Track", + "tv_show": "TV Show", + "url": "Url", + "video": "Video" } } }, From 8a998369d62443df5b83e3c2c6a2ddb452bf0158 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20S=C3=B8rensen?= Date: Sat, 12 Sep 2020 19:15:00 +0200 Subject: [PATCH 35/41] Add padding to rendered template result (#6954) --- src/panels/developer-tools/template/developer-tools-template.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/panels/developer-tools/template/developer-tools-template.ts b/src/panels/developer-tools/template/developer-tools-template.ts index dfabd912d6..c9ca775fbf 100644 --- a/src/panels/developer-tools/template/developer-tools-template.ts +++ b/src/panels/developer-tools/template/developer-tools-template.ts @@ -249,6 +249,7 @@ class HaPanelDevTemplate extends LitElement { clear: both; white-space: pre-wrap; background-color: var(--secondary-background-color); + padding: 8px; } .all_listeners { From 99d0a0a6fdd1490aa4db4984339c0052f4ba6e4f Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Sat, 12 Sep 2020 19:39:54 +0200 Subject: [PATCH 36/41] Lazy load more info content, split logbook and history (#6936) --- gallery/src/components/demo-more-info.js | 2 +- gallery/src/components/more-info-content.ts | 73 ++++++++ gallery/src/demos/demo-more-info-light.ts | 2 +- src/common/const.ts | 1 - src/common/util/throttle.ts | 50 +++++ src/dialogs/more-info/ha-more-info-dialog.ts | 91 ++++++++-- src/dialogs/more-info/ha-more-info-history.ts | 118 +++--------- src/dialogs/more-info/ha-more-info-logbook.ts | 171 ++++++++++++++++++ src/dialogs/more-info/more-info-content.ts | 73 -------- 9 files changed, 391 insertions(+), 190 deletions(-) create mode 100644 gallery/src/components/more-info-content.ts create mode 100644 src/common/util/throttle.ts create mode 100644 src/dialogs/more-info/ha-more-info-logbook.ts delete mode 100644 src/dialogs/more-info/more-info-content.ts diff --git a/gallery/src/components/demo-more-info.js b/gallery/src/components/demo-more-info.js index 9def4e6095..9e4a34023f 100644 --- a/gallery/src/components/demo-more-info.js +++ b/gallery/src/components/demo-more-info.js @@ -2,8 +2,8 @@ import { html } from "@polymer/polymer/lib/utils/html-tag"; /* eslint-plugin-disable lit */ import { PolymerElement } from "@polymer/polymer/polymer-element"; import "../../../src/components/ha-card"; -import "../../../src/dialogs/more-info/more-info-content"; import "../../../src/state-summary/state-card-content"; +import "./more-info-content"; class DemoMoreInfo extends PolymerElement { static get template() { diff --git a/gallery/src/components/more-info-content.ts b/gallery/src/components/more-info-content.ts new file mode 100644 index 0000000000..6d7271155e --- /dev/null +++ b/gallery/src/components/more-info-content.ts @@ -0,0 +1,73 @@ +import { HassEntity } from "home-assistant-js-websocket"; +import { property, PropertyValues, UpdatingElement } from "lit-element"; +import dynamicContentUpdater from "../../../src/common/dom/dynamic_content_updater"; +import { stateMoreInfoType } from "../../../src/common/entity/state_more_info_type"; +import "../../../src/dialogs/more-info/controls/more-info-alarm_control_panel"; +import "../../../src/dialogs/more-info/controls/more-info-automation"; +import "../../../src/dialogs/more-info/controls/more-info-camera"; +import "../../../src/dialogs/more-info/controls/more-info-climate"; +import "../../../src/dialogs/more-info/controls/more-info-configurator"; +import "../../../src/dialogs/more-info/controls/more-info-counter"; +import "../../../src/dialogs/more-info/controls/more-info-cover"; +import "../../../src/dialogs/more-info/controls/more-info-default"; +import "../../../src/dialogs/more-info/controls/more-info-fan"; +import "../../../src/dialogs/more-info/controls/more-info-group"; +import "../../../src/dialogs/more-info/controls/more-info-humidifier"; +import "../../../src/dialogs/more-info/controls/more-info-input_datetime"; +import "../../../src/dialogs/more-info/controls/more-info-light"; +import "../../../src/dialogs/more-info/controls/more-info-lock"; +import "../../../src/dialogs/more-info/controls/more-info-media_player"; +import "../../../src/dialogs/more-info/controls/more-info-person"; +import "../../../src/dialogs/more-info/controls/more-info-script"; +import "../../../src/dialogs/more-info/controls/more-info-sun"; +import "../../../src/dialogs/more-info/controls/more-info-timer"; +import "../../../src/dialogs/more-info/controls/more-info-vacuum"; +import "../../../src/dialogs/more-info/controls/more-info-water_heater"; +import "../../../src/dialogs/more-info/controls/more-info-weather"; +import { HomeAssistant } from "../../../src/types"; + +class MoreInfoContent extends UpdatingElement { + @property({ attribute: false }) public hass?: HomeAssistant; + + @property() public stateObj?: HassEntity; + + private _detachedChild?: ChildNode; + + protected firstUpdated(): void { + this.style.position = "relative"; + this.style.display = "block"; + } + + // This is not a lit element, but an updating element, so we implement update + protected update(changedProps: PropertyValues): void { + super.update(changedProps); + const stateObj = this.stateObj; + const hass = this.hass; + + if (!stateObj || !hass) { + if (this.lastChild) { + this._detachedChild = this.lastChild; + // Detach child to prevent it from doing work. + this.removeChild(this.lastChild); + } + return; + } + + if (this._detachedChild) { + this.appendChild(this._detachedChild); + this._detachedChild = undefined; + } + + const moreInfoType = + stateObj.attributes && "custom_ui_more_info" in stateObj.attributes + ? stateObj.attributes.custom_ui_more_info + : "more-info-" + stateMoreInfoType(stateObj); + + dynamicContentUpdater(this, moreInfoType.toUpperCase(), { + hass, + stateObj, + }); + } +} + +customElements.define("more-info-content", MoreInfoContent); diff --git a/gallery/src/demos/demo-more-info-light.ts b/gallery/src/demos/demo-more-info-light.ts index 19677825d5..8c5ac611e9 100644 --- a/gallery/src/demos/demo-more-info-light.ts +++ b/gallery/src/demos/demo-more-info-light.ts @@ -3,10 +3,10 @@ import { html } from "@polymer/polymer/lib/utils/html-tag"; import { PolymerElement } from "@polymer/polymer/polymer-element"; import "../../../src/components/ha-card"; import { SUPPORT_BRIGHTNESS } from "../../../src/data/light"; -import "../../../src/dialogs/more-info/more-info-content"; import { getEntity } from "../../../src/fake_data/entity"; import { provideHass } from "../../../src/fake_data/provide_hass"; import "../components/demo-more-infos"; +import "../components/more-info-content"; const ENTITIES = [ getEntity("light", "bed_light", "on", { diff --git a/src/common/const.ts b/src/common/const.ts index dc2bf20595..640c4fb13a 100644 --- a/src/common/const.ts +++ b/src/common/const.ts @@ -44,7 +44,6 @@ export const DOMAINS_WITH_MORE_INFO = [ "script", "sun", "timer", - "updater", "vacuum", "water_heater", "weather", diff --git a/src/common/util/throttle.ts b/src/common/util/throttle.ts new file mode 100644 index 0000000000..1cd98ea188 --- /dev/null +++ b/src/common/util/throttle.ts @@ -0,0 +1,50 @@ +// From: underscore.js https://github.com/jashkenas/underscore/blob/master/underscore.js + +// Returns a function, that, when invoked, will only be triggered at most once +// during a given window of time. Normally, the throttled function will run +// as much as it can, without ever going more than once per `wait` duration; +// but if you'd like to disable the execution on the leading edge, pass +// `false for leading`. To disable execution on the trailing edge, ditto. +export const throttle = ( + func: T, + wait: number, + leading = true, + trailing = true +): T => { + let timeout: number | undefined; + let previous = 0; + let context: any; + let args: any; + const later = () => { + previous = leading === false ? 0 : Date.now(); + timeout = undefined; + func.apply(context, args); + if (!timeout) { + context = null; + args = null; + } + }; + // @ts-ignore + return function (...argmnts) { + // @ts-ignore + // @typescript-eslint/no-this-alias + context = this; + args = argmnts; + + const now = Date.now(); + if (!previous && leading === false) { + previous = now; + } + const remaining = wait - (now - previous); + if (remaining <= 0 || remaining > wait) { + if (timeout) { + clearTimeout(timeout); + timeout = undefined; + } + previous = now; + func.apply(context, args); + } else if (!timeout && trailing !== false) { + timeout = window.setTimeout(later, remaining); + } + }; +}; diff --git a/src/dialogs/more-info/ha-more-info-dialog.ts b/src/dialogs/more-info/ha-more-info-dialog.ts index f5beadea90..bb217a732c 100644 --- a/src/dialogs/more-info/ha-more-info-dialog.ts +++ b/src/dialogs/more-info/ha-more-info-dialog.ts @@ -13,10 +13,15 @@ import { } from "lit-element"; import { cache } from "lit-html/directives/cache"; import { isComponentLoaded } from "../../common/config/is_component_loaded"; -import { DOMAINS_MORE_INFO_NO_HISTORY } from "../../common/const"; +import { + DOMAINS_MORE_INFO_NO_HISTORY, + DOMAINS_WITH_MORE_INFO, +} from "../../common/const"; +import { dynamicElement } from "../../common/dom/dynamic-element-directive"; import { fireEvent } from "../../common/dom/fire_event"; import { computeDomain } from "../../common/entity/compute_domain"; import { computeStateName } from "../../common/entity/compute_state_name"; +import { stateMoreInfoType } from "../../common/entity/state_more_info_type"; import { navigate } from "../../common/navigate"; import "../../components/ha-dialog"; import "../../components/ha-header-bar"; @@ -30,21 +35,38 @@ import "../../state-summary/state-card-content"; import { HomeAssistant } from "../../types"; import { showConfirmationDialog } from "../generic/show-dialog-box"; import "./ha-more-info-history"; -import "./more-info-content"; +import "./ha-more-info-logbook"; const DOMAINS_NO_INFO = ["camera", "configurator"]; -const CONTROL_DOMAINS = [ - "light", - "media_player", - "vacuum", - "alarm_control_panel", - "climate", - "humidifier", - "weather", -]; const EDITABLE_DOMAINS_WITH_ID = ["scene", "automation"]; const EDITABLE_DOMAINS = ["script"]; +const MORE_INFO_CONTROL_IMPORT = { + alarm_control_panel: () => import("./controls/more-info-alarm_control_panel"), + automation: () => import("./controls/more-info-automation"), + camera: () => import("./controls/more-info-camera"), + climate: () => import("./controls/more-info-climate"), + configurator: () => import("./controls/more-info-configurator"), + counter: () => import("./controls/more-info-counter"), + cover: () => import("./controls/more-info-cover"), + fan: () => import("./controls/more-info-fan"), + group: () => import("./controls/more-info-group"), + humidifier: () => import("./controls/more-info-humidifier"), + input_datetime: () => import("./controls/more-info-input_datetime"), + light: () => import("./controls/more-info-light"), + lock: () => import("./controls/more-info-lock"), + media_player: () => import("./controls/more-info-media_player"), + person: () => import("./controls/more-info-person"), + script: () => import("./controls/more-info-script"), + sun: () => import("./controls/more-info-sun"), + timer: () => import("./controls/more-info-timer"), + vacuum: () => import("./controls/more-info-vacuum"), + water_heater: () => import("./controls/more-info-water_heater"), + weather: () => import("./controls/more-info-weather"), + hidden: () => {}, + default: () => import("./controls/more-info-default"), +}; + export interface MoreInfoDialogParams { entityId: string | null; } @@ -57,6 +79,8 @@ export class MoreInfoDialog extends LitElement { @internalProperty() private _entityId?: string | null; + @internalProperty() private _moreInfoType?: string; + @internalProperty() private _currTabIndex = 0; public showDialog(params: MoreInfoDialogParams) { @@ -73,6 +97,23 @@ export class MoreInfoDialog extends LitElement { fireEvent(this, "dialog-closed", { dialog: this.localName }); } + protected updated(changedProperties) { + if (!this.hass || !this._entityId || !changedProperties.has("_entityId")) { + return; + } + const stateObj = this.hass.states[this._entityId]; + if (!stateObj) { + return; + } + if (stateObj.attributes && "custom_ui_more_info" in stateObj.attributes) { + this._moreInfoType = stateObj.attributes.custom_ui_more_info; + } else { + const type = stateMoreInfoType(stateObj); + this._moreInfoType = `more-info-${type}`; + MORE_INFO_CONTROL_IMPORT[type](); + } + } + protected render() { if (!this._entityId) { return html``; @@ -137,7 +178,7 @@ export class MoreInfoDialog extends LitElement { ` : ""} - ${CONTROL_DOMAINS.includes(domain) && + ${DOMAINS_WITH_MORE_INFO.includes(domain) && this._computeShowHistoryComponent(entityId) ? html` `} - - ${CONTROL_DOMAINS.includes(domain) || + ${DOMAINS_WITH_MORE_INFO.includes(domain) || !this._computeShowHistoryComponent(entityId) ? "" : html``} + .hass=${this.hass} + .entityId=${this._entityId} + > + `} + ${this._moreInfoType + ? dynamicElement(this._moreInfoType, { + hass: this.hass, + stateObj, + }) + : ""} ${stateObj.attributes.restored ? html`

@@ -210,6 +257,10 @@ export class MoreInfoDialog extends LitElement { .hass=${this.hass} .entityId=${this._entityId} > + ` )}

diff --git a/src/dialogs/more-info/ha-more-info-history.ts b/src/dialogs/more-info/ha-more-info-history.ts index 746bbb4c82..1dc70b68d4 100644 --- a/src/dialogs/more-info/ha-more-info-history.ts +++ b/src/dialogs/more-info/ha-more-info-history.ts @@ -9,14 +9,11 @@ import { TemplateResult, } from "lit-element"; import { isComponentLoaded } from "../../common/config/is_component_loaded"; -import { computeStateDomain } from "../../common/entity/compute_state_domain"; -import "../../components/ha-circular-progress"; +import { throttle } from "../../common/util/throttle"; import "../../components/state-history-charts"; import { getRecentWithCache } from "../../data/cached-history"; import { HistoryResult } from "../../data/history"; -import { getLogbookData, LogbookEntry } from "../../data/logbook"; -import "../../panels/logbook/ha-logbook"; -import { haStyle, haStyleScrollbar } from "../../resources/styles"; +import { haStyle } from "../../resources/styles"; import { HomeAssistant } from "../../types"; @customElement("ha-more-info-history") @@ -27,21 +24,14 @@ export class MoreInfoHistory extends LitElement { @internalProperty() private _stateHistory?: HistoryResult; - @internalProperty() private _entries?: LogbookEntry[]; - - @internalProperty() private _persons = {}; - - private _historyRefreshInterval?: number; + private _throttleGetStateHistory = throttle(() => { + this._getStateHistory(); + }, 10000); protected render(): TemplateResult { if (!this.entityId) { return html``; } - const stateObj = this.hass.states[this.entityId]; - - if (!stateObj) { - return html``; - } return html`${isComponentLoaded(this.hass, "history") ? html`` - : ""} - ${isComponentLoaded(this.hass, "logbook") - ? !this._entries - ? html` - - ` - : this._entries.length - ? html` - - ` - : html`
- ${this.hass.localize("ui.components.logbook.entries_not_found")} -
` : ""} `; } - protected firstUpdated(): void { - this._fetchPersonNames(); - } - protected updated(changedProps: PropertyValues): void { super.updated(changedProps); - if (!this.entityId) { - clearInterval(this._historyRefreshInterval); - } if (changedProps.has("entityId")) { this._stateHistory = undefined; - this._entries = undefined; - this._getStateHistory(); - this._getLogBookData(); + if (!this.entityId) { + return; + } - clearInterval(this._historyRefreshInterval); - this._historyRefreshInterval = window.setInterval(() => { - this._getStateHistory(); - }, 60 * 1000); + this._throttleGetStateHistory(); + return; + } + + if (!this.entityId || !changedProps.has("hass")) { + return; + } + + const oldHass = changedProps.get("hass") as HomeAssistant | undefined; + + if ( + oldHass && + this.hass.states[this.entityId] !== oldHass?.states[this.entityId] + ) { + // wait for commit of data (we only account for the default setting of 1 sec) + setTimeout(this._throttleGetStateHistory, 1000); } } @@ -118,55 +89,14 @@ export class MoreInfoHistory extends LitElement { ); } - private async _getLogBookData() { - if (!isComponentLoaded(this.hass, "logbook")) { - return; - } - const yesterday = new Date(new Date().getTime() - 24 * 60 * 60 * 1000); - const now = new Date(); - this._entries = await getLogbookData( - this.hass, - yesterday.toISOString(), - now.toISOString(), - this.entityId, - true - ); - } - - private _fetchPersonNames() { - Object.values(this.hass.states).forEach((entity) => { - if ( - entity.attributes.user_id && - computeStateDomain(entity) === "person" - ) { - this._persons[entity.attributes.user_id] = - entity.attributes.friendly_name; - } - }); - } - static get styles() { return [ haStyle, - haStyleScrollbar, css` state-history-charts { display: block; margin-bottom: 16px; } - .no-entries { - text-align: center; - padding: 16px; - color: var(--secondary-text-color); - } - ha-logbook { - max-height: 250px; - overflow: auto; - } - ha-circular-progress { - display: flex; - justify-content: center; - } `, ]; } diff --git a/src/dialogs/more-info/ha-more-info-logbook.ts b/src/dialogs/more-info/ha-more-info-logbook.ts new file mode 100644 index 0000000000..a3258133f2 --- /dev/null +++ b/src/dialogs/more-info/ha-more-info-logbook.ts @@ -0,0 +1,171 @@ +import { + css, + customElement, + html, + internalProperty, + LitElement, + property, + PropertyValues, + TemplateResult, +} from "lit-element"; +import { isComponentLoaded } from "../../common/config/is_component_loaded"; +import { computeStateDomain } from "../../common/entity/compute_state_domain"; +import { throttle } from "../../common/util/throttle"; +import "../../components/ha-circular-progress"; +import "../../components/state-history-charts"; +import { getLogbookData, LogbookEntry } from "../../data/logbook"; +import "../../panels/logbook/ha-logbook"; +import { haStyle, haStyleScrollbar } from "../../resources/styles"; +import { HomeAssistant } from "../../types"; + +@customElement("ha-more-info-logbook") +export class MoreInfoLogbook extends LitElement { + @property({ attribute: false }) public hass!: HomeAssistant; + + @property() public entityId!: string; + + @internalProperty() private _logbookEntries?: LogbookEntry[]; + + @internalProperty() private _persons = {}; + + private _lastLogbookDate?: Date; + + private _throttleGetLogbookEntries = throttle(() => { + this._getLogBookData(); + }, 10000); + + protected render(): TemplateResult { + if (!this.entityId) { + return html``; + } + const stateObj = this.hass.states[this.entityId]; + + if (!stateObj) { + return html``; + } + + return html` + ${isComponentLoaded(this.hass, "logbook") + ? !this._logbookEntries + ? html` + + ` + : this._logbookEntries.length + ? html` + + ` + : html`
+ ${this.hass.localize("ui.components.logbook.entries_not_found")} +
` + : ""} + `; + } + + protected firstUpdated(): void { + this._fetchPersonNames(); + } + + protected updated(changedProps: PropertyValues): void { + super.updated(changedProps); + + if (changedProps.has("entityId")) { + this._lastLogbookDate = undefined; + this._logbookEntries = undefined; + + if (!this.entityId) { + return; + } + + this._throttleGetLogbookEntries(); + return; + } + + if (!this.entityId || !changedProps.has("hass")) { + return; + } + + const oldHass = changedProps.get("hass") as HomeAssistant | undefined; + + if ( + oldHass && + this.hass.states[this.entityId] !== oldHass?.states[this.entityId] + ) { + // wait for commit of data (we only account for the default setting of 1 sec) + setTimeout(this._throttleGetLogbookEntries, 1000); + } + } + + private async _getLogBookData() { + if (!isComponentLoaded(this.hass, "logbook")) { + return; + } + const lastDate = + this._lastLogbookDate || + new Date(new Date().getTime() - 24 * 60 * 60 * 1000); + const now = new Date(); + const newEntries = await getLogbookData( + this.hass, + lastDate.toISOString(), + now.toISOString(), + this.entityId, + true + ); + this._logbookEntries = this._logbookEntries + ? [...newEntries, ...this._logbookEntries] + : newEntries; + this._lastLogbookDate = now; + } + + private _fetchPersonNames() { + Object.values(this.hass.states).forEach((entity) => { + if ( + entity.attributes.user_id && + computeStateDomain(entity) === "person" + ) { + this._persons[entity.attributes.user_id] = + entity.attributes.friendly_name; + } + }); + } + + static get styles() { + return [ + haStyle, + haStyleScrollbar, + css` + .no-entries { + text-align: center; + padding: 16px; + color: var(--secondary-text-color); + } + ha-logbook { + max-height: 250px; + overflow: auto; + display: block; + margin-top: 16px; + } + ha-circular-progress { + display: flex; + justify-content: center; + } + `, + ]; + } +} + +declare global { + interface HTMLElementTagNameMap { + "ha-more-info-logbook": MoreInfoLogbook; + } +} diff --git a/src/dialogs/more-info/more-info-content.ts b/src/dialogs/more-info/more-info-content.ts deleted file mode 100644 index 369d9e8d4f..0000000000 --- a/src/dialogs/more-info/more-info-content.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { HassEntity } from "home-assistant-js-websocket"; -import { property, PropertyValues, UpdatingElement } from "lit-element"; -import dynamicContentUpdater from "../../common/dom/dynamic_content_updater"; -import { stateMoreInfoType } from "../../common/entity/state_more_info_type"; -import { HomeAssistant } from "../../types"; -import "./controls/more-info-alarm_control_panel"; -import "./controls/more-info-automation"; -import "./controls/more-info-camera"; -import "./controls/more-info-climate"; -import "./controls/more-info-configurator"; -import "./controls/more-info-counter"; -import "./controls/more-info-cover"; -import "./controls/more-info-default"; -import "./controls/more-info-fan"; -import "./controls/more-info-group"; -import "./controls/more-info-humidifier"; -import "./controls/more-info-input_datetime"; -import "./controls/more-info-light"; -import "./controls/more-info-lock"; -import "./controls/more-info-media_player"; -import "./controls/more-info-person"; -import "./controls/more-info-script"; -import "./controls/more-info-sun"; -import "./controls/more-info-timer"; -import "./controls/more-info-vacuum"; -import "./controls/more-info-water_heater"; -import "./controls/more-info-weather"; - -class MoreInfoContent extends UpdatingElement { - @property({ attribute: false }) public hass?: HomeAssistant; - - @property() public stateObj?: HassEntity; - - private _detachedChild?: ChildNode; - - protected firstUpdated(): void { - this.style.position = "relative"; - this.style.display = "block"; - } - - // This is not a lit element, but an updating element, so we implement update - protected update(changedProps: PropertyValues): void { - super.update(changedProps); - const stateObj = this.stateObj; - const hass = this.hass; - - if (!stateObj || !hass) { - if (this.lastChild) { - this._detachedChild = this.lastChild; - // Detach child to prevent it from doing work. - this.removeChild(this.lastChild); - } - return; - } - - if (this._detachedChild) { - this.appendChild(this._detachedChild); - this._detachedChild = undefined; - } - - const moreInfoType = - stateObj.attributes && "custom_ui_more_info" in stateObj.attributes - ? stateObj.attributes.custom_ui_more_info - : "more-info-" + stateMoreInfoType(stateObj); - - dynamicContentUpdater(this, moreInfoType.toUpperCase(), { - hass, - stateObj, - }); - } -} - -customElements.define("more-info-content", MoreInfoContent); From 5480e5418559e574295877ea0f548cb2e9f84e4b Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Sat, 12 Sep 2020 20:07:22 +0200 Subject: [PATCH 37/41] Mute stream outside of more info (#6959) --- src/components/ha-camera-stream.ts | 10 +++++++--- src/dialogs/more-info/controls/more-info-camera.ts | 2 +- src/panels/config/users/ha-config-users.ts | 9 +++++---- src/panels/lovelace/components/hui-image.ts | 7 ++++--- 4 files changed, 17 insertions(+), 11 deletions(-) diff --git a/src/components/ha-camera-stream.ts b/src/components/ha-camera-stream.ts index b2db579890..7b091055e0 100644 --- a/src/components/ha-camera-stream.ts +++ b/src/components/ha-camera-stream.ts @@ -26,7 +26,11 @@ class HaCameraStream extends LitElement { @property({ attribute: false }) public stateObj?: CameraEntity; - @property({ type: Boolean }) public showControls = false; + @property({ type: Boolean, attribute: "controls" }) + public controls = false; + + @property({ type: Boolean, attribute: "muted" }) + public muted = false; // We keep track if we should force MJPEG with a string // that way it automatically resets if we change entity. @@ -56,9 +60,9 @@ class HaCameraStream extends LitElement { ? html` diff --git a/src/dialogs/more-info/controls/more-info-camera.ts b/src/dialogs/more-info/controls/more-info-camera.ts index 4bc02b6a79..ffd315d9dd 100644 --- a/src/dialogs/more-info/controls/more-info-camera.ts +++ b/src/dialogs/more-info/controls/more-info-camera.ts @@ -48,7 +48,7 @@ class MoreInfoCamera extends LitElement { ${this._cameraPrefs ? html` diff --git a/src/panels/config/users/ha-config-users.ts b/src/panels/config/users/ha-config-users.ts index 2e9432b8d1..30ed2e8093 100644 --- a/src/panels/config/users/ha-config-users.ts +++ b/src/panels/config/users/ha-config-users.ts @@ -1,3 +1,5 @@ +import "@material/mwc-fab"; +import { mdiPlus } from "@mdi/js"; import { customElement, LitElement, @@ -11,7 +13,7 @@ import { DataTableColumnContainer, RowClickedEvent, } from "../../../components/data-table/ha-data-table"; -import "@material/mwc-fab"; +import "../../../components/ha-svg-icon"; import { deleteUser, fetchUsers, updateUser, User } from "../../../data/user"; import { showConfirmationDialog } from "../../../dialogs/generic/show-dialog-box"; import "../../../layouts/hass-tabs-subpage-data-table"; @@ -19,8 +21,6 @@ import { HomeAssistant, Route } from "../../../types"; import { configSections } from "../ha-panel-config"; import { showAddUserDialog } from "./show-dialog-add-user"; import { showUserDetailDialog } from "./show-dialog-user-detail"; -import "../../../components/ha-svg-icon"; -import { mdiPlus } from "@mdi/js"; @customElement("ha-config-users") export class HaConfigUsers extends LitElement { @@ -56,7 +56,7 @@ export class HaConfigUsers extends LitElement { ), sortable: true, filterable: true, - width: "25%", + width: "30%", template: (groupIds) => html` ${this.hass.localize(`groups.${groupIds[0]}`)} `, @@ -66,6 +66,7 @@ export class HaConfigUsers extends LitElement { "ui.panel.config.users.picker.headers.system" ), type: "icon", + width: "80px", sortable: true, filterable: true, template: (generated) => html` diff --git a/src/panels/lovelace/components/hui-image.ts b/src/panels/lovelace/components/hui-image.ts index 004bf705ff..f9133cc612 100644 --- a/src/panels/lovelace/components/hui-image.ts +++ b/src/panels/lovelace/components/hui-image.ts @@ -3,9 +3,9 @@ import { CSSResult, customElement, html, + internalProperty, LitElement, property, - internalProperty, PropertyValues, query, TemplateResult, @@ -16,8 +16,8 @@ import { STATES_OFF } from "../../../common/const"; import parseAspectRatio from "../../../common/util/parse-aspect-ratio"; import "../../../components/ha-camera-stream"; import { fetchThumbnailUrlWithCache } from "../../../data/camera"; -import { CameraEntity, HomeAssistant } from "../../../types"; import { UNAVAILABLE } from "../../../data/entity"; +import { CameraEntity, HomeAssistant } from "../../../types"; const UPDATE_INTERVAL = 10000; const DEFAULT_FILTER = "grayscale(100%)"; @@ -132,8 +132,9 @@ export class HuiImage extends LitElement { ${this.cameraImage && this.cameraView === "live" ? html` ` : html` From c1afed7f98f6c0722157540d78e7fbafbf98b1fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20S=C3=B8rensen?= Date: Sat, 12 Sep 2020 20:34:34 +0200 Subject: [PATCH 38/41] Sort media sources (#6960) Co-authored-by: Bram Kragten --- .../hui-dialog-select-media-player.ts | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/panels/media-browser/hui-dialog-select-media-player.ts b/src/panels/media-browser/hui-dialog-select-media-player.ts index d19cebe0ee..03b53b6a2d 100644 --- a/src/panels/media-browser/hui-dialog-select-media-player.ts +++ b/src/panels/media-browser/hui-dialog-select-media-player.ts @@ -10,10 +10,12 @@ import { TemplateResult, } from "lit-element"; import { fireEvent } from "../../common/dom/fire_event"; +import { computeStateName } from "../../common/entity/compute_state_name"; +import { compare } from "../../common/string/compare"; import { createCloseHeading } from "../../components/ha-dialog"; import { BROWSER_SOURCE } from "../../data/media-player"; -import type { HomeAssistant } from "../../types"; import { haStyleDialog } from "../../resources/styles"; +import type { HomeAssistant } from "../../types"; import type { SelectMediaPlayerDialogParams } from "./show-select-media-source-dialog"; @customElement("hui-dialog-select-media-player") @@ -55,13 +57,15 @@ export class HuiDialogSelectMediaPlayer extends LitElement { "ui.components.media-browser.web-browser" )} - ${this._params.mediaSources.map( - (source) => html` - ${source.attributes.friendly_name} - ` - )} + ${this._params.mediaSources + .sort((a, b) => compare(computeStateName(a), computeStateName(b))) + .map( + (source) => html` + ${computeStateName(source)} + ` + )} `; From 857e4e49d8a32a211e3dc9396ca2294a0442fc2a Mon Sep 17 00:00:00 2001 From: Ludeeus Date: Sat, 12 Sep 2020 18:58:49 +0000 Subject: [PATCH 39/41] Bumped version to 20200912.0 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index f666c7161d..7ee304d284 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ from setuptools import setup, find_packages setup( name="home-assistant-frontend", - version="20200909.0", + version="20200912.0", description="The Home Assistant frontend", url="https://github.com/home-assistant/home-assistant-polymer", author="The Home Assistant Authors", From af0246cd27b414a78eeeb5e82cfab67dc9a216d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20S=C3=B8rensen?= Date: Sat, 12 Sep 2020 21:05:01 +0200 Subject: [PATCH 40/41] convert ha-refresh-tokens-card (#6962) --- .../ha-long-lived-access-tokens-card.ts | 18 ++- src/panels/profile/ha-refresh-tokens-card.js | 126 --------------- src/panels/profile/ha-refresh-tokens-card.ts | 150 ++++++++++++++++++ 3 files changed, 161 insertions(+), 133 deletions(-) delete mode 100644 src/panels/profile/ha-refresh-tokens-card.js create mode 100644 src/panels/profile/ha-refresh-tokens-card.ts diff --git a/src/panels/profile/ha-long-lived-access-tokens-card.ts b/src/panels/profile/ha-long-lived-access-tokens-card.ts index 8c8eb36d96..a12c1a7a20 100644 --- a/src/panels/profile/ha-long-lived-access-tokens-card.ts +++ b/src/panels/profile/ha-long-lived-access-tokens-card.ts @@ -1,4 +1,5 @@ -import "@material/mwc-button"; +import "@material/mwc-button/mwc-button"; +import { mdiDelete } from "@mdi/js"; import { css, CSSResultArray, @@ -12,8 +13,8 @@ import memoizeOne from "memoize-one"; import relativeTime from "../../common/datetime/relative_time"; import { fireEvent } from "../../common/dom/fire_event"; import "../../components/ha-card"; -import "../../components/ha-icon-button"; import "../../components/ha-settings-row"; +import "../../components/ha-svg-icon"; import { RefreshToken } from "../../data/refresh_token"; import { showAlertDialog, @@ -79,11 +80,14 @@ class HaLongLivedTokens extends LitElement { ) )}
- + > + + ` )}
@@ -178,8 +182,8 @@ class HaLongLivedTokens extends LitElement { a { color: var(--primary-color); } - ha-icon-button { - color: var(--primary-text-color); + mwc-button { + --mdc-theme-primary: var(--primary-color); } `, ]; diff --git a/src/panels/profile/ha-refresh-tokens-card.js b/src/panels/profile/ha-refresh-tokens-card.js deleted file mode 100644 index 9c4db68a7a..0000000000 --- a/src/panels/profile/ha-refresh-tokens-card.js +++ /dev/null @@ -1,126 +0,0 @@ -import "@polymer/paper-tooltip/paper-tooltip"; -import { html } from "@polymer/polymer/lib/utils/html-tag"; -/* eslint-plugin-disable lit */ -import { PolymerElement } from "@polymer/polymer/polymer-element"; -import { formatDateTime } from "../../common/datetime/format_date_time"; -import "../../components/ha-card"; -import "../../components/ha-icon-button"; -import "../../components/ha-settings-row"; -import { - showAlertDialog, - showConfirmationDialog, -} from "../../dialogs/generic/show-dialog-box"; -import { EventsMixin } from "../../mixins/events-mixin"; -import LocalizeMixin from "../../mixins/localize-mixin"; - -/* - * @appliesMixin EventsMixin - * @appliesMixin LocalizeMixin - */ -class HaRefreshTokens extends LocalizeMixin(EventsMixin(PolymerElement)) { - static get template() { - return html` - - -
- [[localize('ui.panel.profile.refresh_tokens.description')]] -
- -
- `; - } - - static get properties() { - return { - hass: Object, - refreshTokens: Array, - }; - } - - _computeTokens(refreshTokens) { - return refreshTokens.filter((tkn) => tkn.type === "normal").reverse(); - } - - _formatTitle(clientId) { - return this.localize( - "ui.panel.profile.refresh_tokens.token_title", - "clientId", - clientId - ); - } - - _formatCreatedAt(created) { - return this.localize( - "ui.panel.profile.refresh_tokens.created_at", - "date", - formatDateTime(new Date(created), this.hass.language) - ); - } - - _formatLastUsed(item) { - return item.last_used_at - ? this.localize( - "ui.panel.profile.refresh_tokens.last_used", - "date", - formatDateTime(new Date(item.last_used_at), this.hass.language), - "location", - item.last_used_ip - ) - : this.localize("ui.panel.profile.refresh_tokens.not_used"); - } - - async _handleDelete(ev) { - const token = ev.model.item; - if ( - !(await showConfirmationDialog(this, { - text: this.localize( - "ui.panel.profile.refresh_tokens.confirm_delete", - "name", - token.client_id - ), - })) - ) { - return; - } - try { - await this.hass.callWS({ - type: "auth/delete_refresh_token", - refresh_token_id: token.id, - }); - this.fire("hass-refresh-tokens"); - } catch (err) { - // eslint-disable-next-line - console.error(err); - showAlertDialog(this, { - text: this.localize("ui.panel.profile.refresh_tokens.delete_failed"), - }); - } - } -} - -customElements.define("ha-refresh-tokens-card", HaRefreshTokens); diff --git a/src/panels/profile/ha-refresh-tokens-card.ts b/src/panels/profile/ha-refresh-tokens-card.ts new file mode 100644 index 0000000000..fb8ab60587 --- /dev/null +++ b/src/panels/profile/ha-refresh-tokens-card.ts @@ -0,0 +1,150 @@ +import "@material/mwc-button/mwc-button"; +import { mdiDelete } from "@mdi/js"; +import "@polymer/paper-tooltip/paper-tooltip"; +import { + css, + CSSResultArray, + customElement, + html, + LitElement, + property, + TemplateResult, +} from "lit-element"; +import memoizeOne from "memoize-one"; +import relativeTime from "../../common/datetime/relative_time"; +import { fireEvent } from "../../common/dom/fire_event"; +import "../../components/ha-card"; +import "../../components/ha-settings-row"; +import "../../components/ha-svg-icon"; +import { RefreshToken } from "../../data/refresh_token"; +import { + showAlertDialog, + showConfirmationDialog, +} from "../../dialogs/generic/show-dialog-box"; +import { haStyle } from "../../resources/styles"; +import { HomeAssistant } from "../../types"; + +@customElement("ha-refresh-tokens-card") +class HaRefreshTokens extends LitElement { + @property({ attribute: false }) public hass!: HomeAssistant; + + @property({ attribute: false }) public refreshTokens?: RefreshToken[]; + + private _refreshTokens = memoizeOne( + (refreshTokens: RefreshToken[]): RefreshToken[] => + refreshTokens?.filter((token) => token.type === "normal").reverse() + ); + + protected render(): TemplateResult { + const refreshTokens = this._refreshTokens(this.refreshTokens!); + return html` +
+ ${this.hass.localize("ui.panel.profile.refresh_tokens.description")} + ${refreshTokens?.length + ? refreshTokens!.map( + (token) => html` + ${this.hass.localize( + "ui.panel.profile.refresh_tokens.token_title", + "clientId", + token.client_id + )} + +
+ ${this.hass.localize( + "ui.panel.profile.refresh_tokens.created_at", + "date", + relativeTime(new Date(token.created_at), this.hass.localize) + )} +
+
+ ${token.last_used_at + ? this.hass.localize( + "ui.panel.profile.refresh_tokens.last_used", + "date", + relativeTime( + new Date(token.last_used_at), + this.hass.localize + ), + "location", + token.last_used_ip + ) + : this.hass.localize( + "ui.panel.profile.refresh_tokens.not_used" + )} +
+
+ ${token.is_current + ? html` + ${this.hass.localize( + "ui.panel.profile.refresh_tokens.current_token_tooltip" + )} + ` + : ""} + + + +
+
` + ) + : ""} +
+
`; + } + + private async _deleteToken(ev: Event): Promise { + const token = (ev.currentTarget as any).token; + if ( + !(await showConfirmationDialog(this, { + text: this.hass.localize( + "ui.panel.profile.refresh_tokens.confirm_delete", + "name", + token.client_name + ), + })) + ) { + return; + } + try { + await this.hass.callWS({ + type: "auth/delete_refresh_token", + refresh_token_id: token.id, + }); + fireEvent(this, "hass-refresh-tokens"); + } catch (err) { + await showAlertDialog(this, { + title: this.hass.localize( + "ui.panel.profile.refresh_tokens.delete_failed" + ), + text: err.message, + }); + } + } + + static get styles(): CSSResultArray { + return [ + haStyle, + css` + ha-settings-row { + padding: 0; + } + mwc-button { + --mdc-theme-primary: var(--primary-color); + } + `, + ]; + } +} + +declare global { + interface HTMLElementTagNameMap { + "ha-refresh-tokens-card": HaRefreshTokens; + } +} From 50d37ce4f6f9b48265abc262d17c56c25e750ba0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20S=C3=B8rensen?= Date: Sat, 12 Sep 2020 21:25:40 +0200 Subject: [PATCH 41/41] Remove icon slot (#6964) --- src/components/media-player/ha-media-player-browse.ts | 1 - src/panels/profile/ha-long-lived-access-tokens-card.ts | 2 +- src/panels/profile/ha-refresh-tokens-card.ts | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/components/media-player/ha-media-player-browse.ts b/src/components/media-player/ha-media-player-browse.ts index 7bf868254f..0b14444536 100644 --- a/src/components/media-player/ha-media-player-browse.ts +++ b/src/components/media-player/ha-media-player-browse.ts @@ -206,7 +206,6 @@ export class HaMediaPlayerBrowse extends LitElement { @click=${this._actionClicked} > - + ` )} diff --git a/src/panels/profile/ha-refresh-tokens-card.ts b/src/panels/profile/ha-refresh-tokens-card.ts index fb8ab60587..920d69e3db 100644 --- a/src/panels/profile/ha-refresh-tokens-card.ts +++ b/src/panels/profile/ha-refresh-tokens-card.ts @@ -89,7 +89,7 @@ class HaRefreshTokens extends LitElement { .title=${this.hass.localize(`ui.common.delete`)} @click=${this._deleteToken} > - +
`