From e02e61384e7b924e21e7f01e52e638246c4f43c3 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Wed, 16 Jun 2021 00:48:25 +0000 Subject: [PATCH 01/38] Translation update --- translations/frontend/bg.json | 2 ++ translations/frontend/ca.json | 1 + translations/frontend/cs.json | 1 + translations/frontend/en.json | 6 +++--- translations/frontend/es.json | 1 + translations/frontend/ru.json | 5 +++-- 6 files changed, 11 insertions(+), 5 deletions(-) diff --git a/translations/frontend/bg.json b/translations/frontend/bg.json index d3f1555f7d..12895a2040 100644 --- a/translations/frontend/bg.json +++ b/translations/frontend/bg.json @@ -1052,6 +1052,7 @@ "areas": "Области", "automation": "Автоматизации", "blueprint": "Планове", + "core": "Общи", "customize": "Персонализации", "devices": "Устройства", "entities": "Обекти", @@ -2945,6 +2946,7 @@ "tilt-position": "Наклон" }, "show_header_toggle": "Показване на превключване на заглавка?", + "special_row": "специален ред", "toggle": "Превключване на обекти." }, "entity-filter": { diff --git a/translations/frontend/ca.json b/translations/frontend/ca.json index e7b1e80755..d4a80333ba 100644 --- a/translations/frontend/ca.json +++ b/translations/frontend/ca.json @@ -820,6 +820,7 @@ "was_unplugged": "s'ha desendollat", "was_unsafe": "és insegur" }, + "retrieval_error": "Error durant l'obtenció de l'entrada del diari de registre", "show_trace": "Mostra la traça" }, "media-browser": { diff --git a/translations/frontend/cs.json b/translations/frontend/cs.json index 7243339353..771f43d5b5 100644 --- a/translations/frontend/cs.json +++ b/translations/frontend/cs.json @@ -820,6 +820,7 @@ "was_unplugged": "bylo odpojeno", "was_unsafe": "bylo v nebezpečí" }, + "retrieval_error": "Chyba při načítání záznamu", "show_trace": "Zobrazit trasu" }, "media-browser": { diff --git a/translations/frontend/en.json b/translations/frontend/en.json index 69b5939af7..17b1da1b4d 100644 --- a/translations/frontend/en.json +++ b/translations/frontend/en.json @@ -377,7 +377,7 @@ "error_addon_no_ingress": "The requested add-on does not support ingress", "error_addon_not_found": "Add-on not found", "error_addon_not_installed": "The requested add-on is not installed. Please install it first", - "error_addon_not_started": "The requested add-on are not running. Please start it first", + "error_addon_not_started": "The requested add-on is not running. Please start it first", "faq_link": "My Home Assistant FAQ", "not_supported": "This redirect is not supported by your Home Assistant instance. Check the {link} for the supported redirects and the version they where introduced." }, @@ -820,6 +820,7 @@ "was_unplugged": "was unplugged", "was_unsafe": "was unsafe" }, + "retrieval_error": "Error during logbook entry retrieval", "show_trace": "Show trace" }, "media-browser": { @@ -2990,8 +2991,7 @@ "log_level": "Log Level", "log_level_changed": "Log Level changed to: {level}", "subscribed_to_logs": "Subscribed to Z-Wave JS Log Messages...", - "title": "Z-Wave JS Logs", - "download_logs": "Download Logs" + "title": "Z-Wave JS Logs" }, "navigation": { "logs": "Logs", diff --git a/translations/frontend/es.json b/translations/frontend/es.json index 18bf376a08..f898948e66 100644 --- a/translations/frontend/es.json +++ b/translations/frontend/es.json @@ -820,6 +820,7 @@ "was_unplugged": "se desenchufó", "was_unsafe": "era inseguro" }, + "retrieval_error": "Error durante la recuperación de la entrada del libro de registro", "show_trace": "Mostrar traza" }, "media-browser": { diff --git a/translations/frontend/ru.json b/translations/frontend/ru.json index bd9b26f613..83db274258 100644 --- a/translations/frontend/ru.json +++ b/translations/frontend/ru.json @@ -820,6 +820,7 @@ "was_unplugged": "отключается", "was_unsafe": "не регистрирует безопасность" }, + "retrieval_error": "Ошибка при получении записи в журнале событий", "show_trace": "Показать трассировку" }, "media-browser": { @@ -1263,7 +1264,7 @@ "no_type_provided": "Тип не указан." }, "supervisor": { - "ask": "Обратиться за помощью", + "ask": "Обратитесь за помощью", "observer": "Проверьте Observer", "reboot": "Попробуйте перезагрузить хост", "system_health": "Проверьте статус системы", @@ -3216,7 +3217,7 @@ "error": { "go_back": "Вернуться", "supervisor": { - "ask": "Обратиться за помощью", + "ask": "Обратитесь за помощью", "observer": "Проверить Observer", "reboot": "Попробуйте перезагрузить хост", "system_health": "Проверить статус системы", From 446a9b5c0257d71e4ad714f3a42d7c9397100ca6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20S=C3=B8rensen?= Date: Wed, 16 Jun 2021 11:13:30 +0200 Subject: [PATCH 02/38] Don't clear action on expected error (#9428) --- hassio/src/dialogs/update/dialog-supervisor-update.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hassio/src/dialogs/update/dialog-supervisor-update.ts b/hassio/src/dialogs/update/dialog-supervisor-update.ts index e017c9d2f9..4e27303681 100644 --- a/hassio/src/dialogs/update/dialog-supervisor-update.ts +++ b/hassio/src/dialogs/update/dialog-supervisor-update.ts @@ -158,8 +158,8 @@ class DialogSupervisorUpdate extends LitElement { } catch (err) { if (this.hass.connection.connected && !ignoreSupervisorError(err)) { this._error = extractApiErrorMessage(err); + this._action = null; } - this._action = null; return; } From 7e507b40c4dc05216d8eeb14c79bd7e41899e7da Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Thu, 17 Jun 2021 00:48:10 +0000 Subject: [PATCH 03/38] Translation update --- translations/frontend/bg.json | 15 +++++++++++++-- translations/frontend/de.json | 8 ++++---- translations/frontend/et.json | 1 + translations/frontend/he.json | 7 ++++--- translations/frontend/it.json | 3 ++- translations/frontend/ko.json | 1 + translations/frontend/nb.json | 1 + translations/frontend/pl.json | 1 + translations/frontend/ru.json | 16 ++++++++-------- translations/frontend/zh-Hans.json | 1 + translations/frontend/zh-Hant.json | 1 + 11 files changed, 37 insertions(+), 18 deletions(-) diff --git a/translations/frontend/bg.json b/translations/frontend/bg.json index 12895a2040..a04a0754f6 100644 --- a/translations/frontend/bg.json +++ b/translations/frontend/bg.json @@ -344,7 +344,7 @@ "error": "Възникна неизвестна грешка", "error_addon_not_found": "Не е намерена добавка", "error_addon_not_installed": "Исканата добавка не е инсталирана. Моля, първо я инсталирайте", - "error_addon_not_started": "Заявените добавки не е стартирана. Моля, първо я стартирайте", + "error_addon_not_started": "Заявената добавка не е стартирана. Моля, първо я стартирайте", "faq_link": "My Home Assistant ЧЗВ", "not_supported": "Тази препратка не се поддържа от вашата Home Assistant инсталация. Последвайте {link} за поддържани препратки както и версиите при тяхното пускане." }, @@ -1085,6 +1085,7 @@ "rpi_gpio": "Raspberry Pi GPIO обекти", "scene": "Презареждане на сцените", "script": "Презареждане на скриптовете", + "smtp": "SMTP услуги за уведомяване", "telegram": "Презареждане на Telegram услугите за нотификация", "zone": "Презареждане на зоните" }, @@ -1216,6 +1217,7 @@ }, "description": "Групирайте устройствата и обектите в области", "editor": { + "area_id": "ID на област", "create": "Създаване", "default_name": "Нова област", "delete": "Изтриване", @@ -2240,6 +2242,7 @@ "common": { "controller": "Контролер", "network": "Мрежа", + "node_id": "ID на възел", "query_stage": "Етап на заявка", "wakeup_instructions": "Инструкции за събуждане", "zwave": "Z-Wave" @@ -2247,6 +2250,7 @@ "navigation": { "network": "Мрежа", "node": { + "config": "Конфигуриране", "dashboard": "Табло" }, "nodes": "Възли" @@ -2267,6 +2271,9 @@ "network": { "node_count": "{count} възли" }, + "node_config": { + "header": "Конфигурация на възел" + }, "node_metadata": { "product_manual": "Ръководство за продукта" }, @@ -2291,7 +2298,8 @@ "description": "Това ще накара OpenZWave да разпита дадено устройство и да актуализира командните му класове, възможностите и стойностите му.", "node_status": "Състояние на възела", "refreshing_description": "Опресняване на информацията за възела...", - "step": "Стъпка" + "step": "Стъпка", + "wakeup_header": "Инструкции за събуждане за" }, "select_instance": { "none_found": "Не можахме да намерим OpenZWave инстанция. Ако смятате че не е вярно, проверете Вашите OpenZWare и MQTT и се уверете. че Home Assistant може да комуникира със MQTT брокера." @@ -2327,6 +2335,7 @@ "learn_more": "Научете повече за хората", "no_persons_created_yet": "Изглежда все още не сте добавили хора.", "note_about_persons_configured_in_yaml": "Забележка: хората, конфигурирани чрез configuration.yaml, не могат да бъдат редактирани чрез потребителския интерфейс.", + "person_not_found": "Не успяхме да открием лицето, което се опитвате да редактирате.", "person_not_found_title": "Не е намерено лице" }, "scene": { @@ -2622,6 +2631,7 @@ "zwave_js": { "add_node": { "inclusion_failed": "Възелът не може да бъде добавен. Моля, проверете журналите за повече информация.", + "inclusion_finished": "Възелът е добавен.", "interview_failed": "Разпита на устройството не бе успешен. Допълнителна информация може да е налична в логовете.", "interview_started": "Устройството се разпитва. Това може да отнеме известно време.", "title": "Добавяне на Z-Wave възел" @@ -3062,6 +3072,7 @@ "cardpicker": { "by_card": "По карта", "by_entity": "По обект", + "custom_card": "Персонализирана", "domain": "Домейн", "entity": "Обект", "no_description": "Няма налично описание." diff --git a/translations/frontend/de.json b/translations/frontend/de.json index ff674635c9..1ac790a99c 100644 --- a/translations/frontend/de.json +++ b/translations/frontend/de.json @@ -200,7 +200,7 @@ "manager": "Manager" }, "stage": { - "description": "Add-ons können sich in einer von drei Phasen befinden:\n\n{icon_stable} **Stable**: Dies sind Add-ons, die bereits vollständig sind und Updates erhalten.\n\n{icon_experimental} **Experimentell**: Diese können Fehler enthalten und unvollendet sein.\n\n{icon_deprecated} **Veraltet**: Diese Add-ons erhalten keine Updates mehr.", + "description": "Add-ons können sich in einer von drei Phasen befinden:\n\n{icon_stable} **Stabil**: Dies sind Add-ons, die bereits vollständig sind und Updates erhalten.\n\n{icon_experimental} **Experimentell**: Diese können Fehler enthalten und unvollendet sein.\n\n{icon_deprecated} **Veraltet**: Diese Add-ons erhalten keine Updates mehr.", "title": "Add-on Phase" } }, @@ -235,7 +235,7 @@ } }, "protection_mode": { - "content": "Der Schutzmodus für dieses Add-on ist deaktiviert! Dadurch erhält das Add-on vollen Zugriff auf das gesamte System, was Sicherheitsrisiken mit sich bringt und dein System bei unsachgemäßer Verwendung beschädigen kann. Deaktiviere den Schutzmodus nur, wenn du die Quelle dieses Add-ons kennst, dieses benötigst UND vertraust.", + "content": "Der Schutzmodus für dieses Add-on ist deaktiviert! Dadurch erhält das Add-on vollen Zugriff auf das gesamte System, was Sicherheitsrisiken mit sich bringt und dein System bei unsachgemäßer Verwendung beschädigen kann. Deaktiviere den Schutzmodus nur, wenn du dieses Add-on benötigst und die Quelle dieses Add-ons kennst UND ihr vertraust.", "enable": "Gesicherten Modus einschalten", "title": "Warnung: Der gesicherte Modus ist deaktiviert!" }, @@ -477,7 +477,7 @@ "join_beta_description": "Erhalte Beta-Updates für Home Assistant (RCs), Supervisor und Host", "leave_beta_action": "Beta-Kanal verlassen", "leave_beta_description": "Erhalte stabile Updates für Home Assistant, Supervisor und Host", - "ram_usage": "Arbeitsspeicherbedarf von Supervisor", + "ram_usage": "Arbeitsspeicherbedarf des Supervisors", "reload_supervisor": "Supervisor neu laden", "search": "Suche", "share_diagnostics": "Diagnose teilen", @@ -941,7 +941,7 @@ }, "dialogs": { "config_entry_system_options": { - "enable_new_entities_description": "Wenn deaktiviert werden neu erkannte Entitäten für {integration} nicht automatisch zu Home Assistant hinzugefügt.", + "enable_new_entities_description": "Wenn aktiviert werden neu erkannte Entitäten für {integration} automatisch zu Home Assistant hinzugefügt.", "enable_new_entities_label": "Neu hinzugefügte Entitäten aktivieren.", "enable_polling_description": "Ob Home Assistant automatisch {integration} Entitäten nach Updates abfragen soll.", "enable_polling_label": "Aktiviere Polling für Updates.", diff --git a/translations/frontend/et.json b/translations/frontend/et.json index 4ed9f8720d..912eddb469 100644 --- a/translations/frontend/et.json +++ b/translations/frontend/et.json @@ -820,6 +820,7 @@ "was_unplugged": "eemaldati", "was_unsafe": "oli turvamata" }, + "retrieval_error": "Viga logiraamatu sissekande otsimisel", "show_trace": "Kuva täitmise samme" }, "media-browser": { diff --git a/translations/frontend/he.json b/translations/frontend/he.json index 11f2f3a5a8..995c0e25a3 100644 --- a/translations/frontend/he.json +++ b/translations/frontend/he.json @@ -172,8 +172,8 @@ "title": "מרחב שמות של תהליכי מארח" }, "ingress": { - "description": "תוסף זה משתמש ב-Ingress כדי להטמיע את הממשק שלו בצורה מאובטחת ב-Home Assistant.", - "title": "חדירה" + "description": "תוסף זה משתמש בכניסה כדי להטמיע את הממשק שלו בצורה מאובטחת ב-Home Assistant.", + "title": "כניסה" }, "label": { "apparmor": "apparmor", @@ -377,7 +377,7 @@ "error_addon_no_ingress": "ההרחבה המבוקשת אינו תומכת בכניסה", "error_addon_not_found": "התוסף לא נמצא", "error_addon_not_installed": "התוסף המבוקש אינו מותקן. אנא התקן אותו תחילה", - "error_addon_not_started": "התוסף המבוקש אינו פועל. אנא הפעל אותו תחילה", + "error_addon_not_started": "ההרחבה המבוקשת אינה פועלת. נא הפעל אותו תחילה", "faq_link": "שאלות נפוצות על Home Assistant שלי", "not_supported": "הפניה זו אינה נתמכת על ידי מופע ה-Home Assistant שלך. בדוק ב-{link} את ההפניות הנתמכות ואת הגרסה שהוצגה." }, @@ -820,6 +820,7 @@ "was_unplugged": "היה מנותק", "was_unsafe": "היה לא בטוח" }, + "retrieval_error": "שגיאה במהלך אחזור ערך יומן רישום", "show_trace": "הצג מעקב" }, "media-browser": { diff --git a/translations/frontend/it.json b/translations/frontend/it.json index 349aee0be3..ee8c41af03 100644 --- a/translations/frontend/it.json +++ b/translations/frontend/it.json @@ -377,7 +377,7 @@ "error_addon_no_ingress": "Il componente aggiuntivo richiesto non supporta l'ingresso", "error_addon_not_found": "Componente aggiuntivo non trovato", "error_addon_not_installed": "Il componente aggiuntivo richiesto non è installato. Si prega di installarlo prima di proseguire", - "error_addon_not_started": "Il componente aggiuntivo non è in esecuzione. Si prega di avviarlo prima di proseguire", + "error_addon_not_started": "Il componente aggiuntivo richiesto non è in esecuzione. Per favore, avvialo prima", "faq_link": "My Home Assistant FAQ", "not_supported": "Questo reindirizzamento non è supportato dall'istanza di Home Assistant. Controlla il {link} per i reindirizzamenti supportati e la versione in cui sono stati introdotti." }, @@ -820,6 +820,7 @@ "was_unplugged": "è stato scollegato", "was_unsafe": "non era sicuro" }, + "retrieval_error": "Errore durante il recupero della voce del registro", "show_trace": "Mostra la traccia" }, "media-browser": { diff --git a/translations/frontend/ko.json b/translations/frontend/ko.json index 9887dc0e6e..819ce983e3 100644 --- a/translations/frontend/ko.json +++ b/translations/frontend/ko.json @@ -820,6 +820,7 @@ "was_unplugged": "의 플러그가 뽑혔습니다.", "was_unsafe": "이(가) 안전하지 않은 상태가 되었습니다." }, + "retrieval_error": "로그북 항목 검색 중 오류", "show_trace": "추적 표시하기" }, "media-browser": { diff --git a/translations/frontend/nb.json b/translations/frontend/nb.json index 109a6f4ade..ca09cccc72 100644 --- a/translations/frontend/nb.json +++ b/translations/frontend/nb.json @@ -820,6 +820,7 @@ "was_unplugged": "ble frakoblet", "was_unsafe": "var utrygt" }, + "retrieval_error": "Feil under henting av loggbokoppføring", "show_trace": "Vis spor" }, "media-browser": { diff --git a/translations/frontend/pl.json b/translations/frontend/pl.json index 8b393e69c6..56f387bf39 100644 --- a/translations/frontend/pl.json +++ b/translations/frontend/pl.json @@ -820,6 +820,7 @@ "was_unplugged": "nastąpiło odłączenie", "was_unsafe": "wykryto zagrożenie" }, + "retrieval_error": "Błąd podczas pobierania wpisów z dziennika", "show_trace": "Pokaż ślad" }, "media-browser": { diff --git a/translations/frontend/ru.json b/translations/frontend/ru.json index 83db274258..6254ed7396 100644 --- a/translations/frontend/ru.json +++ b/translations/frontend/ru.json @@ -411,18 +411,18 @@ "ssl": "SSL" }, "folders": "Папки", - "full_snapshot": "Все файлы", + "full_snapshot": "Полный снимок", "name": "Название", "no_snapshots": "Не найдено ни одного снимка", - "partial_snapshot": "Выбрать из списка", + "partial_snapshot": "Частичный снимок", "password": "Пароль", "password_protected": "защищено паролем", "password_protection": "Защита паролем", "passwords_not_matching": "Пароли не совпадают", "security": "Безопасность", - "select_type": "Выберите что нужно восстановить из этого снимка файловой системы", + "select_type": "Выберите что нужно восстановить:", "selected": "Выбрано: {number}", - "type": "Выберите что должен включать в себя снимок файловой системы", + "type": "Тип снимка файловой системы:", "upload_snapshot": "Загрузить снимок на сервер" }, "store": { @@ -3896,15 +3896,15 @@ "confirm_password": "Подтверждение пароля", "description": "Восстановить предыдущее состояние из снимка файловой системы (snapshot)", "folders": "Папки", - "full_snapshot": "Все файлы", + "full_snapshot": "Полный снимок", "hide_log": "Скрыть журнал", "in_progress": "Восстановление системы", - "partial_snapshot": "Выбрать из списка", + "partial_snapshot": "Частичный снимок", "password": "Пароль", "password_protection": "Защита паролем", - "select_type": "Выберите что нужно восстановить из этого снимка файловой системы", + "select_type": "Выберите что нужно восстановить:", "show_log": "Показать журнал", - "type": "Выберите что должен включать в себя снимок файловой системы" + "type": "Тип снимка файловой системы:" }, "user": { "create_account": "Создать учётную запись", diff --git a/translations/frontend/zh-Hans.json b/translations/frontend/zh-Hans.json index c78698ffc7..a2d807aa69 100644 --- a/translations/frontend/zh-Hans.json +++ b/translations/frontend/zh-Hans.json @@ -820,6 +820,7 @@ "was_unplugged": "已拔出", "was_unsafe": "[%key_id:6884523%]" }, + "retrieval_error": "获取日志条目时出错", "show_trace": "显示轨迹" }, "media-browser": { diff --git a/translations/frontend/zh-Hant.json b/translations/frontend/zh-Hant.json index f76637002b..ff4f4f9cfb 100644 --- a/translations/frontend/zh-Hant.json +++ b/translations/frontend/zh-Hant.json @@ -820,6 +820,7 @@ "was_unplugged": "狀態為拔下", "was_unsafe": "狀態為不安全" }, + "retrieval_error": "日誌登錄檢索出現錯誤", "show_trace": "顯示紀錄" }, "media-browser": { From 5ad95cad9020d242c6d09a0debd5d33d50845cfd Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Thu, 17 Jun 2021 15:23:08 +0200 Subject: [PATCH 04/38] Support white color mode (#9386) --- src/data/light.ts | 1 + .../more-info/controls/more-info-light.ts | 46 ++++++++++++++----- 2 files changed, 36 insertions(+), 11 deletions(-) diff --git a/src/data/light.ts b/src/data/light.ts index 9ef41c284f..d9c68e5d44 100644 --- a/src/data/light.ts +++ b/src/data/light.ts @@ -8,6 +8,7 @@ export enum LightColorModes { ONOFF = "onoff", BRIGHTNESS = "brightness", COLOR_TEMP = "color_temp", + WHITE = "white", HS = "hs", XY = "xy", RGB = "rgb", diff --git a/src/dialogs/more-info/controls/more-info-light.ts b/src/dialogs/more-info/controls/more-info-light.ts index 3ac4d2e143..b232bc7c3c 100644 --- a/src/dialogs/more-info/controls/more-info-light.ts +++ b/src/dialogs/more-info/controls/more-info-light.ts @@ -9,6 +9,7 @@ import { TemplateResult, } from "lit"; import { customElement, property, state } from "lit/decorators"; +import memoizeOne from "memoize-one"; import { supportsFeature } from "../../../common/entity/supports-feature"; import "../../../components/ha-attributes"; import "../../../components/ha-button-toggle-group"; @@ -28,11 +29,6 @@ import { } from "../../../data/light"; import type { HomeAssistant } from "../../../types"; -const toggleButtons = [ - { label: "Color", value: "color" }, - { label: "Temperature", value: LightColorModes.COLOR_TEMP }, -]; - @customElement("more-info-light") class MoreInfoLight extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; @@ -59,7 +55,7 @@ class MoreInfoLight extends LitElement { @state() private _colorPickerColor?: [number, number, number]; - @state() private _mode?: "color" | LightColorModes.COLOR_TEMP; + @state() private _mode?: "color" | LightColorModes; protected render(): TemplateResult { if (!this.hass || !this.stateObj) { @@ -71,6 +67,11 @@ class MoreInfoLight extends LitElement { LightColorModes.COLOR_TEMP ); + const supportsWhite = lightSupportsColorMode( + this.stateObj, + LightColorModes.WHITE + ); + const supportsRgbww = lightSupportsColorMode( this.stateObj, LightColorModes.RGBWW @@ -101,16 +102,17 @@ class MoreInfoLight extends LitElement { ${this.stateObj.state === "on" ? html` ${supportsTemp || supportsColor ? html`
` : ""} - ${supportsTemp && supportsColor + ${supportsColor && (supportsTemp || supportsWhite) ? html`` : ""} ${supportsTemp && - (!supportsColor || this._mode === LightColorModes.COLOR_TEMP) + ((!supportsColor && !supportsWhite) || + this._mode === LightColorModes.COLOR_TEMP) ? html` ` : ""} - ${supportsColor && (!supportsTemp || this._mode === "color") + ${supportsColor && + ((!supportsTemp && !supportsWhite) || this._mode === "color") ? html`
{ + const modes = [{ label: "Color", value: "color" }]; + if (supportsTemp) { + modes.push({ label: "Temperature", value: LightColorModes.COLOR_TEMP }); + } + if (supportsWhite) { + modes.push({ label: "White", value: LightColorModes.WHITE }); + } + return modes; + } + ); + private _modeChanged(ev: CustomEvent) { this._mode = ev.detail.value; } @@ -326,6 +342,14 @@ class MoreInfoLight extends LitElement { this._brightnessSliderValue = bri; + if (this._mode === LightColorModes.WHITE) { + this.hass.callService("light", "turn_on", { + entity_id: this.stateObj!.entity_id, + white: Math.min(255, Math.round((bri * 255) / 100)), + }); + return; + } + if (this._brightnessAdjusted) { const rgb = this.stateObj!.attributes.rgb_color || From 9fbd594f37581f75e8c6a93914b36e30d15e1f04 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Thu, 17 Jun 2021 15:23:20 +0200 Subject: [PATCH 05/38] Check if `/proc/version` exists (#9438) --- build-scripts/gulp/webpack.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/build-scripts/gulp/webpack.js b/build-scripts/gulp/webpack.js index 6f0c450fac..e7083cd578 100644 --- a/build-scripts/gulp/webpack.js +++ b/build-scripts/gulp/webpack.js @@ -19,10 +19,12 @@ const bothBuilds = (createConfigFunc, params) => [ createConfigFunc({ ...params, latestBuild: false }), ]; -const isWsl = fs - .readFileSync("/proc/version", "utf-8") - .toLocaleLowerCase() - .includes("microsoft"); +const isWsl = + fs.existsSync("/proc/version") && + fs + .readFileSync("/proc/version", "utf-8") + .toLocaleLowerCase() + .includes("microsoft"); /** * @param {{ From ce3c8f264d69335f51268bef91ced8d73a3d1eec Mon Sep 17 00:00:00 2001 From: Raman Gupta <7243222+raman325@users.noreply.github.com> Date: Thu, 17 Jun 2021 11:19:18 -0400 Subject: [PATCH 06/38] Update zwave_js log subscription to handle core changes (#9262) --- src/data/zwave_js.ts | 18 +++++++++++++++--- .../zwave_js/zwave_js-logs.ts | 15 +++++++++------ 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/src/data/zwave_js.ts b/src/data/zwave_js.ts index a8389c2c26..6a112b7060 100644 --- a/src/data/zwave_js.ts +++ b/src/data/zwave_js.ts @@ -193,6 +193,18 @@ export const getIdentifiersFromDevice = ( }; }; +export type ZWaveJSLogUpdate = ZWaveJSLogMessageUpdate | ZWaveJSLogConfigUpdate; + +interface ZWaveJSLogMessageUpdate { + type: "log_message"; + log_message: ZWaveJSLogMessage; +} + +interface ZWaveJSLogConfigUpdate { + type: "log_config"; + log_config: ZWaveJSLogConfig; +} + export interface ZWaveJSLogMessage { timestamp: string; level: string; @@ -203,10 +215,10 @@ export interface ZWaveJSLogMessage { export const subscribeZWaveJSLogs = ( hass: HomeAssistant, entry_id: string, - callback: (message: ZWaveJSLogMessage) => void + callback: (update: ZWaveJSLogUpdate) => void ) => - hass.connection.subscribeMessage(callback, { - type: "zwave_js/subscribe_logs", + hass.connection.subscribeMessage(callback, { + type: "zwave_js/subscribe_log_updates", entry_id, }); diff --git a/src/panels/config/integrations/integration-panels/zwave_js/zwave_js-logs.ts b/src/panels/config/integrations/integration-panels/zwave_js/zwave_js-logs.ts index 41d0e7172e..0e380a18b3 100644 --- a/src/panels/config/integrations/integration-panels/zwave_js/zwave_js-logs.ts +++ b/src/panels/config/integrations/integration-panels/zwave_js/zwave_js-logs.ts @@ -33,16 +33,20 @@ class ZWaveJSLogs extends SubscribeMixin(LitElement) { public hassSubscribe(): Array> { return [ - subscribeZWaveJSLogs(this.hass, this.configEntryId, (log) => { + subscribeZWaveJSLogs(this.hass, this.configEntryId, (update) => { if (!this.hasUpdated) { return; } - if (Array.isArray(log.message)) { - for (const line of log.message) { - this._textarea!.value += `${line}\n`; + if (update.type === "log_message") { + if (Array.isArray(update.log_message.message)) { + for (const line of update.log_message.message) { + this._textarea!.value += `${line}\n`; + } + } else { + this._textarea!.value += `${update.log_message.message}\n`; } } else { - this._textarea!.value += `${log.message}\n`; + this._logConfig = update.log_config; } }).then((unsub) => { this._textarea!.value += `${this.hass.localize( @@ -141,7 +145,6 @@ class ZWaveJSLogs extends SubscribeMixin(LitElement) { return; } setZWaveJSLogLevel(this.hass!, this.configEntryId, selected); - this._logConfig.level = selected; this._textarea!.value += `${this.hass.localize( "ui.panel.config.zwave_js.logs.log_level_changed", { level: selected.charAt(0).toUpperCase() + selected.slice(1) } From f88e238d4152c7b47c7caf94022b7f483bb76387 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Fri, 18 Jun 2021 00:48:41 +0000 Subject: [PATCH 07/38] Translation update --- translations/frontend/nl.json | 5 ++-- translations/frontend/sl.json | 43 +++++++++++++++++++++++++++++++++-- 2 files changed, 44 insertions(+), 4 deletions(-) diff --git a/translations/frontend/nl.json b/translations/frontend/nl.json index 4a7bf66ba8..149728e21e 100644 --- a/translations/frontend/nl.json +++ b/translations/frontend/nl.json @@ -820,6 +820,7 @@ "was_unplugged": "is niet aangesloten", "was_unsafe": "was onveilig" }, + "retrieval_error": "Fout tijdens het ophalen van de logboekregel", "show_trace": "Tracering weergeven" }, "media-browser": { @@ -2089,7 +2090,7 @@ "unknown_error": "Onbekende fout", "unnamed_device": "Naamloos apparaat", "update": "Bijwerken", - "update_device_error": "Updaten van het apparaat mislukt" + "update_device_error": "Bijwerken van het apparaat mislukt" }, "entities": { "caption": "Entiteiten", @@ -3147,7 +3148,7 @@ "available_events": "Beschikbare gebeurtenissen", "count_listeners": " ({count} luisteraars)", "data": "Gebeurtenis data (YAML, optioneel)", - "description": "Start een evenement op de Home Assistant-gebeurtenisbus", + "description": "Vermeld een gebeurtenis in de gebeurtenisbus.", "documentation": "Gebeurtenissen documentatie", "event_fired": "Gebeurtenis {name} uitgevoerd", "fire_event": "Gebeurtenis uitvoeren", diff --git a/translations/frontend/sl.json b/translations/frontend/sl.json index ed306ac1d9..b78bcbcbb5 100644 --- a/translations/frontend/sl.json +++ b/translations/frontend/sl.json @@ -102,6 +102,25 @@ "unknown": "Neznano" } }, + "supervisor": { + "dialog": { + "hardware": { + "attributes": "Lastnosti", + "id": "ID", + "search": "Iskanje naprav", + "subsystem": "Podsistem", + "title": "Strojna oprema" + } + }, + "snapshot": { + "created": "Ustvarjeno" + }, + "system": { + "supervisor": { + "search": "Iskanje" + } + } + }, "ui": { "auth_store": { "ask": "Ali želite ostati prijavljeni?", @@ -309,6 +328,9 @@ "no_match": "Ni ustreznih območji", "show_areas": "Prikaži območja" }, + "attributes": { + "expansion_header": "Lastnosti" + }, "blueprint-picker": { "add_user": "Dodaj uporabnika", "remove_user": "Odstrani uporabnika", @@ -746,6 +768,10 @@ "zha_device_card": { "device_name_placeholder": "Spremenite ime naprave" } + }, + "zha_reconfigure_device": { + "attribute": "Atribut", + "button_show": "Pokaži podrobnosti" } }, "duration": { @@ -791,7 +817,8 @@ "caption": "Območja", "data_table": { "area": "Območje", - "devices": "Naprave" + "devices": "Naprave", + "entities": "Entitete" }, "delete": { "confirmation_text": "Vse naprave na tem območju bodo postale nedodeljene.", @@ -803,6 +830,7 @@ "create": "Ustvari", "default_name": "Novo območje", "delete": "Izbriši", + "linked_entities_caption": "Entitete", "name": "Ime", "name_required": "Ime je obvezno", "unknown_error": "Neznana napaka", @@ -1214,6 +1242,7 @@ "title": "Alexa" }, "connected": "Povezan", + "connecting": "Povezovanje...", "connection_status": "Stanje povezave v oblaku", "fetching_subscription": "Pridobivam naročnino...", "google": { @@ -1242,6 +1271,7 @@ "remote": { "access_is_being_prepared": "Oddaljeni dostop se pripravlja. Obvestili vas bomo, ko bo pripravljen.", "certificate_info": "Informacije o potrdilu", + "connected": "Povezan", "info": "Home Assistant Cloud zagotavlja varno oddaljeno povezavo do vašega primerka, medtem ko ste zunaj doma.", "instance_is_available": "Vaš primerek je na voljo na", "instance_will_be_available": "Vaš primerek bo na voljo na", @@ -1512,7 +1542,8 @@ "scripts": "Skripte", "unknown_error": "Neznana napaka", "unnamed_device": "Neimenovana naprava", - "update": "Posodobite" + "update": "Posodobite", + "update_device_error": "Posodabljanje naprave ni uspelo" }, "entities": { "caption": "Entitete", @@ -1983,6 +2014,7 @@ "add_scene": "Dodajanje scene", "delete_confirm": "Ali ste prepričani, da želite izbrisati to sceno?", "delete_scene": "Brisanje scene", + "duplicate": "Podvoji", "edit_scene": "Urejanje scene", "header": "Urejevalnik scen", "headers": { @@ -2316,6 +2348,7 @@ "zwave_info": "Z-Wave Informacije" }, "navigation": { + "logs": "Dnevniki", "network": "Omrežje" }, "network_status": { @@ -3271,6 +3304,12 @@ "primary_color": "Primarna barva", "reset": "Ponastavi" }, + "time_format": { + "formats": { + "12": "12 ur (AM / PM)", + "24": "24 ur" + } + }, "vibrate": { "description": "V tej napravi omogočite ali onemogočite vibracije pri krmiljenju naprav.", "header": "Vibriraj" From 0ef3421fa23185a93ad639e6a3854f656e629807 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Fri, 18 Jun 2021 11:15:07 +0200 Subject: [PATCH 08/38] Bump chartjs to version 3 (#9401) --- build-scripts/bundle.js | 2 - package.json | 4 +- src/common/color/colors.ts | 63 ++ src/common/color/rgb.ts | 2 +- src/common/datetime/format_date.ts | 13 + src/common/number/clamp.ts | 2 + src/common/style/icon_color_css.ts | 13 +- src/common/util/throttle.ts | 32 +- src/components/chart/chart-date-adapter.ts | 197 ++++++ src/components/chart/ha-chart-base.ts | 229 ++++++ .../chart/state-history-chart-line.ts | 392 +++++++++++ .../chart/state-history-chart-timeline.ts | 310 ++++++++ .../{ => chart}/state-history-charts.ts | 16 +- src/components/chart/timeline-chart/const.ts | 18 + .../chart/timeline-chart/textbar-element.ts | 60 ++ .../timeline-chart/timeline-controller.ts | 160 +++++ .../chart/timeline-chart/timeline-scale.ts | 55 ++ src/components/entity/ha-chart-base.js | 661 ------------------ src/components/state-history-chart-line.js | 433 ------------ .../state-history-chart-timeline.js | 286 -------- src/dialogs/more-info/ha-more-info-history.ts | 2 +- src/dialogs/more-info/ha-more-info-logbook.ts | 1 - src/panels/history/ha-panel-history.ts | 2 +- .../lovelace/cards/hui-history-graph-card.ts | 6 +- src/panels/lovelace/cards/hui-map-card.ts | 17 +- .../lovelace/cards/hui-thermostat-card.ts | 30 +- src/resources/chartjs.ts | 35 + src/resources/ha-chart-scripts.js | 60 -- src/resources/ha-style.ts | 30 +- src/resources/styles.ts | 5 +- yarn.lock | 48 +- 31 files changed, 1616 insertions(+), 1568 deletions(-) create mode 100644 src/common/color/colors.ts create mode 100644 src/common/number/clamp.ts create mode 100644 src/components/chart/chart-date-adapter.ts create mode 100644 src/components/chart/ha-chart-base.ts create mode 100644 src/components/chart/state-history-chart-line.ts create mode 100644 src/components/chart/state-history-chart-timeline.ts rename src/components/{ => chart}/state-history-charts.ts (88%) create mode 100644 src/components/chart/timeline-chart/const.ts create mode 100644 src/components/chart/timeline-chart/textbar-element.ts create mode 100644 src/components/chart/timeline-chart/timeline-controller.ts create mode 100644 src/components/chart/timeline-chart/timeline-scale.ts delete mode 100644 src/components/entity/ha-chart-base.js delete mode 100644 src/components/state-history-chart-line.js delete mode 100644 src/components/state-history-chart-timeline.js create mode 100644 src/resources/chartjs.ts delete mode 100644 src/resources/ha-chart-scripts.js diff --git a/build-scripts/bundle.js b/build-scripts/bundle.js index dc4b59b02a..cd6e1ba854 100644 --- a/build-scripts/bundle.js +++ b/build-scripts/bundle.js @@ -5,8 +5,6 @@ const paths = require("./paths.js"); // Files from NPM Packages that should not be imported module.exports.ignorePackages = ({ latestBuild }) => [ - // Bloats bundle and it's not used. - path.resolve(require.resolve("moment"), "../locale"), // Part of yaml.js and only used for !!js functions that we don't use require.resolve("esprima"), ]; diff --git a/package.json b/package.json index 5e99d75f81..934e858b68 100644 --- a/package.json +++ b/package.json @@ -96,11 +96,11 @@ "@vibrant/quantizer-mmcq": "^3.2.1-alpha.1", "@vue/web-component-wrapper": "^1.2.0", "@webcomponents/webcomponentsjs": "^2.2.7", - "chart.js": "^2.9.4", - "chartjs-chart-timeline": "^0.4.0", + "chart.js": "^3.3.2", "comlink": "^4.3.1", "core-js": "^3.6.5", "cropperjs": "^1.5.11", + "date-fns": "^2.22.1", "deep-clone-simple": "^1.1.1", "deep-freeze": "^0.0.1", "fecha": "^4.2.0", diff --git a/src/common/color/colors.ts b/src/common/color/colors.ts new file mode 100644 index 0000000000..d70aa5ce6a --- /dev/null +++ b/src/common/color/colors.ts @@ -0,0 +1,63 @@ +export const COLORS = [ + "#377eb8", + "#984ea3", + "#00d2d5", + "#ff7f00", + "#af8d00", + "#7f80cd", + "#b3e900", + "#c42e60", + "#a65628", + "#f781bf", + "#8dd3c7", + "#bebada", + "#fb8072", + "#80b1d3", + "#fdb462", + "#fccde5", + "#bc80bd", + "#ffed6f", + "#c4eaff", + "#cf8c00", + "#1b9e77", + "#d95f02", + "#e7298a", + "#e6ab02", + "#a6761d", + "#0097ff", + "#00d067", + "#f43600", + "#4ba93b", + "#5779bb", + "#927acc", + "#97ee3f", + "#bf3947", + "#9f5b00", + "#f48758", + "#8caed6", + "#f2b94f", + "#eff26e", + "#e43872", + "#d9b100", + "#9d7a00", + "#698cff", + "#d9d9d9", + "#00d27e", + "#d06800", + "#009f82", + "#c49200", + "#cbe8ff", + "#fecddf", + "#c27eb6", + "#8cd2ce", + "#c4b8d9", + "#f883b0", + "#a49100", + "#f48800", + "#27d0df", + "#a04a9b", +]; + +export function getColorByIndex(index: number) { + return COLORS[index % COLORS.length]; +} diff --git a/src/common/color/rgb.ts b/src/common/color/rgb.ts index 54b69408dc..8e83eab6a0 100644 --- a/src/common/color/rgb.ts +++ b/src/common/color/rgb.ts @@ -1,4 +1,4 @@ -const luminosity = (rgb: [number, number, number]): number => { +export const luminosity = (rgb: [number, number, number]): number => { // http://www.w3.org/TR/WCAG20/#relativeluminancedef const lum: [number, number, number] = [0, 0, 0]; for (let i = 0; i < rgb.length; i++) { diff --git a/src/common/datetime/format_date.ts b/src/common/datetime/format_date.ts index fa82187a46..e9b70430a0 100644 --- a/src/common/datetime/format_date.ts +++ b/src/common/datetime/format_date.ts @@ -17,6 +17,19 @@ export const formatDate = toLocaleDateStringSupportsOptions formatDateMem(locale).format(dateObj) : (dateObj: Date) => format(dateObj, "longDate"); +const formatDateShortMem = memoizeOne( + (locale: FrontendLocaleData) => + new Intl.DateTimeFormat(locale.language, { + day: "numeric", + month: "short", + }) +); + +export const formatDateShort = toLocaleDateStringSupportsOptions + ? (dateObj: Date, locale: FrontendLocaleData) => + formatDateShortMem(locale).format(dateObj) + : (dateObj: Date) => format(dateObj, "shortDate"); + const formatDateWeekdayMem = memoizeOne( (locale: FrontendLocaleData) => new Intl.DateTimeFormat(locale.language, { diff --git a/src/common/number/clamp.ts b/src/common/number/clamp.ts new file mode 100644 index 0000000000..4368d20add --- /dev/null +++ b/src/common/number/clamp.ts @@ -0,0 +1,2 @@ +export const clamp = (value: number, min: number, max: number) => + Math.min(Math.max(value, min), max); diff --git a/src/common/style/icon_color_css.ts b/src/common/style/icon_color_css.ts index fbc663653a..b636ffa89c 100644 --- a/src/common/style/icon_color_css.ts +++ b/src/common/style/icon_color_css.ts @@ -29,31 +29,28 @@ export const iconColorCSS = css` } ha-icon[data-domain="climate"][data-state="cooling"] { - color: var(--cool-color, #2b9af9); + color: var(--cool-color, var(--state-climate-cool-color)); } ha-icon[data-domain="climate"][data-state="heating"] { - color: var(--heat-color, #ff8100); + color: var(--heat-color, var(--state-climate-heat-color)); } ha-icon[data-domain="climate"][data-state="drying"] { - color: var(--dry-color, #efbd07); + color: var(--dry-color, var(--state-climate-dry-color)); } ha-icon[data-domain="alarm_control_panel"] { color: var(--alarm-color-armed, var(--label-badge-red)); } - ha-icon[data-domain="alarm_control_panel"][data-state="disarmed"] { color: var(--alarm-color-disarmed, var(--label-badge-green)); } - ha-icon[data-domain="alarm_control_panel"][data-state="pending"], ha-icon[data-domain="alarm_control_panel"][data-state="arming"] { color: var(--alarm-color-pending, var(--label-badge-yellow)); animation: pulse 1s infinite; } - ha-icon[data-domain="alarm_control_panel"][data-state="triggered"] { color: var(--alarm-color-triggered, var(--label-badge-red)); animation: pulse 1s infinite; @@ -73,11 +70,11 @@ export const iconColorCSS = css` ha-icon[data-domain="plant"][data-state="problem"], ha-icon[data-domain="zwave"][data-state="dead"] { - color: var(--error-state-color, #db4437); + color: var(--state-icon-error-color); } /* Color the icon if unavailable */ ha-icon[data-state="unavailable"] { - color: var(--state-icon-unavailable-color); + color: var(--state-unavailable-color); } `; diff --git a/src/common/util/throttle.ts b/src/common/util/throttle.ts index 4832a2709b..2860f66be5 100644 --- a/src/common/util/throttle.ts +++ b/src/common/util/throttle.ts @@ -5,32 +5,20 @@ // 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 = unknown>( - func: T, +export const throttle = ( + func: (...args: T) => void, 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; - + return (...args: T): void => { + const later = () => { + previous = leading === false ? 0 : Date.now(); + timeout = undefined; + func(...args); + }; const now = Date.now(); if (!previous && leading === false) { previous = now; @@ -42,7 +30,7 @@ export const throttle = unknown>( timeout = undefined; } previous = now; - func.apply(context, args); + func(...args); } else if (!timeout && trailing !== false) { timeout = window.setTimeout(later, remaining); } diff --git a/src/components/chart/chart-date-adapter.ts b/src/components/chart/chart-date-adapter.ts new file mode 100644 index 0000000000..a25163b662 --- /dev/null +++ b/src/components/chart/chart-date-adapter.ts @@ -0,0 +1,197 @@ +import { _adapters } from "chart.js"; +import { + startOfSecond, + startOfMinute, + startOfHour, + startOfDay, + startOfWeek, + startOfMonth, + startOfQuarter, + startOfYear, + addMilliseconds, + addSeconds, + addMinutes, + addHours, + addDays, + addWeeks, + addMonths, + addQuarters, + addYears, + differenceInMilliseconds, + differenceInSeconds, + differenceInMinutes, + differenceInHours, + differenceInDays, + differenceInWeeks, + differenceInMonths, + differenceInQuarters, + differenceInYears, + endOfSecond, + endOfMinute, + endOfHour, + endOfDay, + endOfWeek, + endOfMonth, + endOfQuarter, + endOfYear, +} from "date-fns"; +import { formatDate, formatDateShort } from "../../common/datetime/format_date"; +import { + formatDateTime, + formatDateTimeWithSeconds, +} from "../../common/datetime/format_date_time"; +import { + formatTime, + formatTimeWithSeconds, +} from "../../common/datetime/format_time"; + +const FORMATS = { + datetime: "datetime", + datetimeseconds: "datetimeseconds", + millisecond: "millisecond", + second: "second", + minute: "minute", + hour: "hour", + day: "day", + week: "week", + month: "month", + quarter: "quarter", + year: "year", +}; + +_adapters._date.override({ + formats: () => FORMATS, + parse: (value: Date | number) => { + if (!(value instanceof Date)) { + return value; + } + return value.getTime(); + }, + format: function (time, fmt: keyof typeof FORMATS) { + switch (fmt) { + case "datetime": + return formatDateTime(new Date(time), this.options.locale); + case "datetimeseconds": + return formatDateTimeWithSeconds(new Date(time), this.options.locale); + case "millisecond": + return formatTimeWithSeconds(new Date(time), this.options.locale); + case "second": + return formatTimeWithSeconds(new Date(time), this.options.locale); + case "minute": + return formatTime(new Date(time), this.options.locale); + case "hour": + return formatTime(new Date(time), this.options.locale); + case "day": + return formatDateShort(new Date(time), this.options.locale); + case "week": + return formatDate(new Date(time), this.options.locale); + case "month": + return formatDate(new Date(time), this.options.locale); + case "quarter": + return formatDate(new Date(time), this.options.locale); + case "year": + return formatDate(new Date(time), this.options.locale); + default: + return ""; + } + }, + // @ts-ignore + add: (time, amount, unit) => { + switch (unit) { + case "millisecond": + return addMilliseconds(time, amount); + case "second": + return addSeconds(time, amount); + case "minute": + return addMinutes(time, amount); + case "hour": + return addHours(time, amount); + case "day": + return addDays(time, amount); + case "week": + return addWeeks(time, amount); + case "month": + return addMonths(time, amount); + case "quarter": + return addQuarters(time, amount); + case "year": + return addYears(time, amount); + default: + return time; + } + }, + diff: (max, min, unit) => { + switch (unit) { + case "millisecond": + return differenceInMilliseconds(max, min); + case "second": + return differenceInSeconds(max, min); + case "minute": + return differenceInMinutes(max, min); + case "hour": + return differenceInHours(max, min); + case "day": + return differenceInDays(max, min); + case "week": + return differenceInWeeks(max, min); + case "month": + return differenceInMonths(max, min); + case "quarter": + return differenceInQuarters(max, min); + case "year": + return differenceInYears(max, min); + default: + return 0; + } + }, + // @ts-ignore + startOf: (time, unit, weekday) => { + switch (unit) { + case "second": + return startOfSecond(time); + case "minute": + return startOfMinute(time); + case "hour": + return startOfHour(time); + case "day": + return startOfDay(time); + case "week": + return startOfWeek(time); + case "isoWeek": + return startOfWeek(time, { + weekStartsOn: +weekday! as 0 | 1 | 2 | 3 | 4 | 5 | 6, + }); + case "month": + return startOfMonth(time); + case "quarter": + return startOfQuarter(time); + case "year": + return startOfYear(time); + default: + return time; + } + }, + // @ts-ignore + endOf: (time, unit) => { + switch (unit) { + case "second": + return endOfSecond(time); + case "minute": + return endOfMinute(time); + case "hour": + return endOfHour(time); + case "day": + return endOfDay(time); + case "week": + return endOfWeek(time); + case "month": + return endOfMonth(time); + case "quarter": + return endOfQuarter(time); + case "year": + return endOfYear(time); + default: + return time; + } + }, +}); diff --git a/src/components/chart/ha-chart-base.ts b/src/components/chart/ha-chart-base.ts new file mode 100644 index 0000000000..a976083c34 --- /dev/null +++ b/src/components/chart/ha-chart-base.ts @@ -0,0 +1,229 @@ +import type { + Chart, + ChartType, + ChartData, + ChartOptions, + TooltipModel, +} from "chart.js"; +import { css, CSSResultGroup, html, LitElement, PropertyValues } from "lit"; +import { customElement, property, state } from "lit/decorators"; +import { classMap } from "lit/directives/class-map"; +import { styleMap } from "lit/directives/style-map"; +import { clamp } from "../../common/number/clamp"; + +@customElement("ha-chart-base") +export default class HaChartBase extends LitElement { + public chart?: Chart; + + @property() + public chartType: ChartType = "line"; + + @property({ attribute: false }) + public data: ChartData = { datasets: [] }; + + @property({ attribute: false }) + public options?: ChartOptions; + + @state() private _tooltip?: TooltipModel; + + @state() private _height?: string; + + protected firstUpdated() { + this._setupChart(); + } + + public willUpdate(changedProps: PropertyValues): void { + super.willUpdate(changedProps); + + if (!this.hasUpdated || !this.chart) { + return; + } + + if (changedProps.has("type")) { + this.chart.config.type = this.chartType; + } + + if (changedProps.has("data")) { + this.chart.data = this.data; + } + if (changedProps.has("options")) { + this.chart.options = this._createOptions(); + } + this.chart.update("none"); + } + + protected render() { + return html` +
+ + ${this._tooltip + ? html`
+
${this._tooltip.title}
+ ${this._tooltip.beforeBody + ? html`
+ ${this._tooltip.beforeBody} +
` + : ""} +
+
    + ${this._tooltip.body.map( + (item, i) => html`
  • + ${item.lines.join("\n")} +
  • ` + )} +
+
+
` + : ""} +
+ `; + } + + private async _setupChart() { + const ctx: CanvasRenderingContext2D = this.renderRoot + .querySelector("canvas")! + .getContext("2d")!; + + this.chart = new (await import("../../resources/chartjs")).Chart(ctx, { + type: this.chartType, + data: this.data, + options: this._createOptions(), + plugins: [ + { + id: "afterRenderHook", + afterRender: (chart) => { + this._height = `${chart.height}px`; + }, + }, + ], + }); + } + + private _createOptions() { + return { + ...this.options, + plugins: { + ...this.options?.plugins, + tooltip: { + ...this.options?.plugins?.tooltip, + enabled: false, + external: (context) => this._handleTooltip(context), + }, + }, + }; + } + + private _handleTooltip(context: { + chart: Chart; + tooltip: TooltipModel; + }) { + if (context.tooltip.opacity === 0) { + this._tooltip = undefined; + return; + } + this._tooltip = { ...context.tooltip }; + } + + public updateChart = (): void => { + if (this.chart) { + this.chart.update(); + } + }; + + static get styles(): CSSResultGroup { + return css` + :host { + display: block; + } + .chartContainer { + position: relative; + overflow: hidden; + height: 0; + transition: height 300ms cubic-bezier(0.4, 0, 0.2, 1); + } + .chartTooltip { + padding: 4px; + font-size: 90%; + position: absolute; + background: rgba(80, 80, 80, 0.9); + color: white; + border-radius: 4px; + pointer-events: none; + transform: translate(-50%, 12px); + z-index: 1000; + width: 200px; + transition: opacity 0.15s ease-in-out; + } + :host([rtl]) .chartTooltip { + direction: rtl; + } + .chartTooltip ul { + display: inline-block; + padding: 0 0px; + margin: 5px 0 0 0; + width: 100%; + } + .chartTooltip ul { + margin: 0 3px; + } + .chartTooltip li { + display: block; + white-space: pre-line; + } + .chartTooltip li::first-line { + line-height: 0; + } + .chartTooltip .title { + text-align: center; + font-weight: 500; + } + .chartTooltip .beforeBody { + text-align: center; + font-weight: 300; + word-break: break-all; + } + .chartTooltip em { + border-radius: 4px; + display: inline-block; + height: 10px; + margin-right: 4px; + width: 10px; + } + :host([rtl]) .chartTooltip em { + margin-right: inherit; + margin-left: 4px; + } + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + "ha-chart-base": HaChartBase; + } +} diff --git a/src/components/chart/state-history-chart-line.ts b/src/components/chart/state-history-chart-line.ts new file mode 100644 index 0000000000..24733ce3f4 --- /dev/null +++ b/src/components/chart/state-history-chart-line.ts @@ -0,0 +1,392 @@ +import type { ChartData, ChartDataset, ChartOptions } from "chart.js"; +import { html, LitElement, PropertyValues } from "lit"; +import { property, state } from "lit/decorators"; +import { getColorByIndex } from "../../common/color/colors"; +import { LineChartEntity, LineChartState } from "../../data/history"; +import { HomeAssistant } from "../../types"; +import "./ha-chart-base"; + +class StateHistoryChartLine extends LitElement { + @property({ attribute: false }) public hass!: HomeAssistant; + + @property({ attribute: false }) public data: LineChartEntity[] = []; + + @property({ type: Boolean }) public names = false; + + @property() public unit?: string; + + @property() public identifier?: string; + + @property({ type: Boolean }) public isSingleDevice = false; + + @property({ attribute: false }) public endTime?: Date; + + @state() private _chartData?: ChartData<"line">; + + @state() private _chartOptions?: ChartOptions<"line">; + + protected render() { + return html` + + `; + } + + public willUpdate(changedProps: PropertyValues) { + if (!this.hasUpdated) { + this._chartOptions = { + parsing: false, + animation: false, + scales: { + x: { + type: "time", + adapters: { + date: { + locale: this.hass.locale, + }, + }, + ticks: { + maxRotation: 0, + sampleSize: 5, + autoSkipPadding: 20, + major: { + enabled: true, + }, + font: (context) => + context.tick && context.tick.major + ? ({ weight: "bold" } as any) + : {}, + }, + time: { + tooltipFormat: "datetimeseconds", + }, + }, + y: { + ticks: { + maxTicksLimit: 7, + }, + title: { + display: true, + text: this.unit, + }, + }, + }, + plugins: { + tooltip: { + mode: "nearest", + callbacks: { + label: (context) => + `${context.dataset.label}: ${context.parsed.y} ${this.unit}`, + }, + }, + filler: { + propagate: true, + }, + legend: { + display: !this.isSingleDevice, + labels: { + usePointStyle: true, + }, + }, + }, + hover: { + mode: "nearest", + }, + elements: { + line: { + tension: 0.1, + borderWidth: 1.5, + }, + point: { + hitRadius: 5, + }, + }, + }; + } + if (changedProps.has("data")) { + this._generateData(); + } + } + + private _generateData() { + let colorIndex = 0; + const computedStyles = getComputedStyle(this); + const deviceStates = this.data; + const datasets: ChartDataset<"line">[] = []; + let endTime: Date; + + if (deviceStates.length === 0) { + return; + } + + function safeParseFloat(value) { + const parsed = parseFloat(value); + return isFinite(parsed) ? parsed : null; + } + + endTime = + this.endTime || + // Get the highest date from the last date of each device + new Date( + Math.max.apply( + null, + deviceStates.map((devSts) => + new Date( + devSts.states[devSts.states.length - 1].last_changed + ).getMilliseconds() + ) + ) + ); + if (endTime > new Date()) { + endTime = new Date(); + } + + const names = this.names || {}; + deviceStates.forEach((states) => { + const domain = states.domain; + const name = names[states.entity_id] || states.name; + // array containing [value1, value2, etc] + let prevValues: any[] | null = null; + + const data: ChartDataset<"line">[] = []; + + const pushData = (timestamp: Date, datavalues: any[] | null) => { + if (!datavalues) return; + if (timestamp > endTime) { + // Drop datapoints that are after the requested endTime. This could happen if + // endTime is "now" and client time is not in sync with server time. + return; + } + data.forEach((d, i) => { + if (datavalues[i] === null && prevValues && prevValues[i] !== null) { + // null data values show up as gaps in the chart. + // If the current value for the dataset is null and the previous + // value of the data set is not null, then add an 'end' point + // to the chart for the previous value. Otherwise the gap will + // be too big. It will go from the start of the previous data + // value until the start of the next data value. + d.data.push({ x: timestamp.getTime(), y: prevValues[i] }); + } + d.data.push({ x: timestamp.getTime(), y: datavalues[i] }); + }); + prevValues = datavalues; + }; + + const addDataSet = ( + nameY: string, + step = false, + fill = false, + color?: string + ) => { + if (!color) { + color = getColorByIndex(colorIndex); + colorIndex++; + } + data.push({ + label: nameY, + fill: fill ? "origin" : false, + borderColor: color, + backgroundColor: color + "7F", + stepped: step ? "before" : false, + pointRadius: 0, + data: [], + }); + }; + + if ( + domain === "thermostat" || + domain === "climate" || + domain === "water_heater" + ) { + const hasHvacAction = states.states.some( + (entityState) => entityState.attributes?.hvac_action + ); + + const isHeating = + domain === "climate" && hasHvacAction + ? (entityState: LineChartState) => + entityState.attributes?.hvac_action === "heating" + : (entityState: LineChartState) => entityState.state === "heat"; + const isCooling = + domain === "climate" && hasHvacAction + ? (entityState: LineChartState) => + entityState.attributes?.hvac_action === "cooling" + : (entityState: LineChartState) => entityState.state === "cool"; + + const hasHeat = states.states.some(isHeating); + const hasCool = states.states.some(isCooling); + // We differentiate between thermostats that have a target temperature + // range versus ones that have just a target temperature + + // Using step chart by step-before so manually interpolation not needed. + const hasTargetRange = states.states.some( + (entityState) => + entityState.attributes && + entityState.attributes.target_temp_high !== + entityState.attributes.target_temp_low + ); + addDataSet( + `${this.hass.localize("ui.card.climate.current_temperature", { + name: name, + })}`, + true + ); + if (hasHeat) { + addDataSet( + `${this.hass.localize("ui.card.climate.heating", { name: name })}`, + true, + true, + computedStyles.getPropertyValue("--state-climate-heat-color") + ); + // The "heating" series uses steppedArea to shade the area below the current + // temperature when the thermostat is calling for heat. + } + if (hasCool) { + addDataSet( + `${this.hass.localize("ui.card.climate.cooling", { name: name })}`, + true, + true, + computedStyles.getPropertyValue("--state-climate-cool-color") + ); + // The "cooling" series uses steppedArea to shade the area below the current + // temperature when the thermostat is calling for heat. + } + + if (hasTargetRange) { + addDataSet( + `${this.hass.localize("ui.card.climate.target_temperature_mode", { + name: name, + mode: this.hass.localize("ui.card.climate.high"), + })}`, + true + ); + addDataSet( + `${this.hass.localize("ui.card.climate.target_temperature_mode", { + name: name, + mode: this.hass.localize("ui.card.climate.low"), + })}`, + true + ); + } else { + addDataSet( + `${this.hass.localize("ui.card.climate.target_temperature_entity", { + name: name, + })}`, + true + ); + } + + states.states.forEach((entityState) => { + if (!entityState.attributes) return; + const curTemp = safeParseFloat( + entityState.attributes.current_temperature + ); + const series = [curTemp]; + if (hasHeat) { + series.push(isHeating(entityState) ? curTemp : null); + } + if (hasCool) { + series.push(isCooling(entityState) ? curTemp : null); + } + if (hasTargetRange) { + const targetHigh = safeParseFloat( + entityState.attributes.target_temp_high + ); + const targetLow = safeParseFloat( + entityState.attributes.target_temp_low + ); + series.push(targetHigh, targetLow); + pushData(new Date(entityState.last_changed), series); + } else { + const target = safeParseFloat(entityState.attributes.temperature); + series.push(target); + pushData(new Date(entityState.last_changed), series); + } + }); + } else if (domain === "humidifier") { + addDataSet( + `${this.hass.localize("ui.card.humidifier.target_humidity_entity", { + name: name, + })}`, + true + ); + addDataSet( + `${this.hass.localize("ui.card.humidifier.on_entity", { + name: name, + })}`, + true, + true + ); + + states.states.forEach((entityState) => { + if (!entityState.attributes) return; + const target = safeParseFloat(entityState.attributes.humidity); + const series = [target]; + series.push(entityState.state === "on" ? target : null); + pushData(new Date(entityState.last_changed), series); + }); + } else { + // Only disable interpolation for sensors + const isStep = domain === "sensor"; + addDataSet(name, isStep); + + let lastValue: number; + let lastDate: Date; + let lastNullDate: Date | null = null; + + // Process chart data. + // When state is `unknown`, calculate the value and break the line. + states.states.forEach((entityState) => { + const value = safeParseFloat(entityState.state); + const date = new Date(entityState.last_changed); + if (value !== null && lastNullDate) { + const dateTime = date.getTime(); + const lastNullDateTime = lastNullDate.getTime(); + const lastDateTime = lastDate?.getTime(); + const tmpValue = + (value - lastValue) * + ((lastNullDateTime - lastDateTime) / + (dateTime - lastDateTime)) + + lastValue; + pushData(lastNullDate, [tmpValue]); + pushData(new Date(lastNullDateTime + 1), [null]); + pushData(date, [value]); + lastDate = date; + lastValue = value; + lastNullDate = null; + } else if (value !== null && lastNullDate === null) { + pushData(date, [value]); + lastDate = date; + lastValue = value; + } else if ( + value === null && + lastNullDate === null && + lastValue !== undefined + ) { + lastNullDate = date; + } + }); + } + + // Add an entry for final values + pushData(endTime, prevValues); + + // Concat two arrays + Array.prototype.push.apply(datasets, data); + }); + + this._chartData = { + datasets, + }; + } +} +customElements.define("state-history-chart-line", StateHistoryChartLine); + +declare global { + interface HTMLElementTagNameMap { + "state-history-chart-line": StateHistoryChartLine; + } +} diff --git a/src/components/chart/state-history-chart-timeline.ts b/src/components/chart/state-history-chart-timeline.ts new file mode 100644 index 0000000000..ee2fc7d007 --- /dev/null +++ b/src/components/chart/state-history-chart-timeline.ts @@ -0,0 +1,310 @@ +import type { ChartData, ChartDataset, ChartOptions } from "chart.js"; +import { HassEntity } from "home-assistant-js-websocket"; +import { html, LitElement, PropertyValues } from "lit"; +import { customElement, property, state } from "lit/decorators"; +import { getColorByIndex } from "../../common/color/colors"; +import { formatDateTimeWithSeconds } from "../../common/datetime/format_date_time"; +import { computeDomain } from "../../common/entity/compute_domain"; +import { computeRTL } from "../../common/util/compute_rtl"; +import { TimelineEntity } from "../../data/history"; +import { HomeAssistant } from "../../types"; +import "./ha-chart-base"; +import type { TimeLineData } from "./timeline-chart/const"; + +/** Binary sensor device classes for which the static colors for on/off need to be inverted. + * List the ones were "off" = good or normal state = should be rendered "green". + */ +const BINARY_SENSOR_DEVICE_CLASS_COLOR_INVERTED = new Set([ + "battery", + "door", + "garage_door", + "gas", + "lock", + "opening", + "problem", + "safety", + "smoke", + "window", +]); + +const STATIC_STATE_COLORS = new Set([ + "on", + "off", + "home", + "not_home", + "unavailable", + "unknown", + "idle", +]); + +const stateColorMap: Map = new Map(); + +let colorIndex = 0; + +const invertOnOff = (entityState?: HassEntity) => + entityState && + computeDomain(entityState.entity_id) === "binary_sensor" && + "device_class" in entityState.attributes && + BINARY_SENSOR_DEVICE_CLASS_COLOR_INVERTED.has( + entityState.attributes.device_class! + ); + +const getColor = ( + stateString: string, + entityState: HassEntity, + computedStyles: CSSStyleDeclaration +) => { + if (invertOnOff(entityState)) { + stateString = stateString === "on" ? "off" : "on"; + } + if (stateColorMap.has(stateString)) { + return stateColorMap.get(stateString); + } + if (STATIC_STATE_COLORS.has(stateString)) { + const color = computedStyles.getPropertyValue( + `--state-${stateString}-color` + ); + stateColorMap.set(stateString, color); + return color; + } + const color = getColorByIndex(colorIndex); + colorIndex++; + stateColorMap.set(stateString, color); + return color; +}; + +@customElement("state-history-chart-timeline") +export class StateHistoryChartTimeline extends LitElement { + @property({ attribute: false }) public hass!: HomeAssistant; + + @property({ attribute: false }) public data: TimelineEntity[] = []; + + @property({ type: Boolean }) public names = false; + + @property() public unit?: string; + + @property() public identifier?: string; + + @property({ type: Boolean }) public isSingleDevice = false; + + @property({ attribute: false }) public endTime?: Date; + + @state() private _chartData?: ChartData<"timeline">; + + @state() private _chartOptions?: ChartOptions<"timeline">; + + protected render() { + return html` + + `; + } + + public willUpdate(changedProps: PropertyValues) { + if (!this.hasUpdated) { + this._chartOptions = { + maintainAspectRatio: false, + parsing: false, + animation: false, + scales: { + x: { + type: "timeline", + position: "bottom", + adapters: { + date: { + locale: this.hass.locale, + }, + }, + ticks: { + autoSkip: true, + maxRotation: 0, + sampleSize: 5, + autoSkipPadding: 20, + major: { + enabled: true, + }, + font: (context) => + context.tick && context.tick.major + ? ({ weight: "bold" } as any) + : {}, + }, + grid: { + offset: false, + }, + time: { + tooltipFormat: "datetimeseconds", + }, + }, + y: { + type: "category", + barThickness: 20, + offset: true, + grid: { + display: false, + drawBorder: false, + drawTicks: false, + }, + ticks: { + display: this.data.length !== 1, + }, + afterSetDimensions: (y) => { + y.maxWidth = y.chart.width * 0.18; + }, + position: computeRTL(this.hass) ? "right" : "left", + }, + }, + plugins: { + tooltip: { + mode: "nearest", + callbacks: { + title: (context) => + context![0].chart!.data!.labels![ + context[0].datasetIndex + ] as string, + beforeBody: (context) => context[0].dataset.label || "", + label: (item) => { + const d = item.dataset.data[item.dataIndex] as TimeLineData; + return [ + d.label || "", + formatDateTimeWithSeconds(d.start, this.hass.locale), + formatDateTimeWithSeconds(d.end, this.hass.locale), + ]; + }, + labelColor: (item) => ({ + borderColor: (item.dataset.data[item.dataIndex] as TimeLineData) + .color!, + backgroundColor: (item.dataset.data[ + item.dataIndex + ] as TimeLineData).color!, + }), + }, + }, + filler: { + propagate: true, + }, + }, + }; + } + if (changedProps.has("data")) { + this._generateData(); + } + } + + private _generateData() { + const computedStyles = getComputedStyle(this); + let stateHistory = this.data; + + if (!stateHistory) { + stateHistory = []; + } + + const startTime = new Date( + stateHistory.reduce( + (minTime, stateInfo) => + Math.min(minTime, new Date(stateInfo.data[0].last_changed).getTime()), + new Date().getTime() + ) + ); + + // end time is Math.max(startTime, last_event) + let endTime = + this.endTime || + new Date( + stateHistory.reduce( + (maxTime, stateInfo) => + Math.max( + maxTime, + new Date( + stateInfo.data[stateInfo.data.length - 1].last_changed + ).getTime() + ), + startTime.getTime() + ) + ); + + if (endTime > new Date()) { + endTime = new Date(); + } + + const labels: string[] = []; + const datasets: ChartDataset<"timeline">[] = []; + const names = this.names || {}; + // stateHistory is a list of lists of sorted state objects + stateHistory.forEach((stateInfo) => { + let newLastChanged: Date; + let prevState: string | null = null; + let locState: string | null = null; + let prevLastChanged = startTime; + const entityDisplay: string = + names[stateInfo.entity_id] || stateInfo.name; + + const dataRow: TimeLineData[] = []; + stateInfo.data.forEach((entityState) => { + let newState: string | null = entityState.state; + const timeStamp = new Date(entityState.last_changed); + if (!newState) { + newState = null; + } + if (timeStamp > endTime) { + // Drop datapoints that are after the requested endTime. This could happen if + // endTime is 'now' and client time is not in sync with server time. + return; + } + if (prevState === null) { + prevState = newState; + locState = entityState.state_localize; + prevLastChanged = new Date(entityState.last_changed); + } else if (newState !== prevState) { + newLastChanged = new Date(entityState.last_changed); + + dataRow.push({ + start: prevLastChanged, + end: newLastChanged, + label: locState, + color: getColor( + prevState, + this.hass.states[stateInfo.entity_id], + computedStyles + ), + }); + + prevState = newState; + locState = entityState.state_localize; + prevLastChanged = newLastChanged; + } + }); + + if (prevState !== null) { + dataRow.push({ + start: prevLastChanged, + end: endTime, + label: locState, + color: getColor( + prevState, + this.hass.states[stateInfo.entity_id], + computedStyles + ), + }); + } + datasets.push({ + data: dataRow, + label: stateInfo.entity_id, + }); + labels.push(entityDisplay); + }); + + this._chartData = { + labels: labels, + datasets: datasets, + }; + } +} + +declare global { + interface HTMLElementTagNameMap { + "state-history-chart-timeline": StateHistoryChartTimeline; + } +} diff --git a/src/components/state-history-charts.ts b/src/components/chart/state-history-charts.ts similarity index 88% rename from src/components/state-history-charts.ts rename to src/components/chart/state-history-charts.ts index 9be40220da..c31559c838 100644 --- a/src/components/state-history-charts.ts +++ b/src/components/chart/state-history-charts.ts @@ -7,10 +7,10 @@ import { TemplateResult, } from "lit"; import { customElement, property } from "lit/decorators"; -import { isComponentLoaded } from "../common/config/is_component_loaded"; -import { HistoryResult } from "../data/history"; -import type { HomeAssistant } from "../types"; -import "./ha-circular-progress"; +import { isComponentLoaded } from "../../common/config/is_component_loaded"; +import { HistoryResult } from "../../data/history"; +import type { HomeAssistant } from "../../types"; +import "../ha-circular-progress"; import "./state-history-chart-line"; import "./state-history-chart-timeline"; @@ -24,7 +24,7 @@ class StateHistoryCharts extends LitElement { @property({ attribute: false }) public endTime?: Date; - @property({ type: Boolean }) public upToNow = false; + @property({ type: Boolean, attribute: "up-to-now" }) public upToNow = false; @property({ type: Boolean, attribute: "no-single" }) public noSingle = false; @@ -101,12 +101,12 @@ class StateHistoryCharts extends LitElement { return css` :host { display: block; - /* height of single timeline chart = 58px */ - min-height: 58px; + /* height of single timeline chart = 60px */ + min-height: 60px; } .info { text-align: center; - line-height: 58px; + line-height: 60px; color: var(--secondary-text-color); } `; diff --git a/src/components/chart/timeline-chart/const.ts b/src/components/chart/timeline-chart/const.ts new file mode 100644 index 0000000000..ac5f234272 --- /dev/null +++ b/src/components/chart/timeline-chart/const.ts @@ -0,0 +1,18 @@ +export interface TimeLineData { + start: Date; + end: Date; + label?: string | null; + color?: string; +} + +declare module "chart.js" { + interface ChartTypeRegistry { + timeline: { + chartOptions: BarControllerChartOptions; + datasetOptions: BarControllerDatasetOptions; + defaultDataPoint: TimeLineData; + parsedDataType: any; + scales: "timeline"; + }; + } +} diff --git a/src/components/chart/timeline-chart/textbar-element.ts b/src/components/chart/timeline-chart/textbar-element.ts new file mode 100644 index 0000000000..1348021b0e --- /dev/null +++ b/src/components/chart/timeline-chart/textbar-element.ts @@ -0,0 +1,60 @@ +import { BarElement, BarOptions, BarProps } from "chart.js"; +import { hex2rgb } from "../../../common/color/convert-color"; +import { luminosity } from "../../../common/color/rgb"; + +export interface TextBarProps extends BarProps { + text?: string | null; + options?: Partial; +} + +export interface TextBaroptions extends BarOptions { + textPad?: number; + textColor?: string; + backgroundColor: string; +} + +export class TextBarElement extends BarElement { + static id = "textbar"; + + draw(ctx) { + super.draw(ctx); + const options = this.options as TextBaroptions; + const { x, y, base, width, text } = (this as BarElement< + TextBarProps, + TextBaroptions + >).getProps(["x", "y", "base", "width", "text"]); + + if (!text) { + return; + } + + ctx.beginPath(); + const textRect = ctx.measureText(text); + if ( + textRect.width === 0 || + textRect.width + (options.textPad || 4) + 2 > width + ) { + return; + } + const textColor = + options.textColor || + (options.backgroundColor && + (luminosity(hex2rgb(options.backgroundColor)) > 0.5 ? "#000" : "#fff")); + + // ctx.font = "12px arial"; + ctx.fillStyle = textColor; + ctx.lineWidth = 0; + ctx.strokeStyle = textColor; + ctx.textBaseline = "middle"; + ctx.fillText( + text, + x - width / 2 + (options.textPad || 4), + y + (base - y) / 2 + ); + } + + tooltipPosition(useFinalPosition: boolean) { + const { x, y, base } = this.getProps(["x", "y", "base"], useFinalPosition); + return { x, y: y + (base - y) / 2 }; + } +} diff --git a/src/components/chart/timeline-chart/timeline-controller.ts b/src/components/chart/timeline-chart/timeline-controller.ts new file mode 100644 index 0000000000..6b6ce7c41b --- /dev/null +++ b/src/components/chart/timeline-chart/timeline-controller.ts @@ -0,0 +1,160 @@ +import { BarController, BarElement } from "chart.js"; +import { TimeLineData } from "./const"; +import { TextBarProps } from "./textbar-element"; + +function parseValue(entry, item, vScale, i) { + const startValue = vScale.parse(entry.start, i); + const endValue = vScale.parse(entry.end, i); + const min = Math.min(startValue, endValue); + const max = Math.max(startValue, endValue); + let barStart = min; + let barEnd = max; + + if (Math.abs(min) > Math.abs(max)) { + barStart = max; + barEnd = min; + } + + // Store `barEnd` (furthest away from origin) as parsed value, + // to make stacking straight forward + item[vScale.axis] = barEnd; + + item._custom = { + barStart, + barEnd, + start: startValue, + end: endValue, + min, + max, + }; + + return item; +} + +export class TimelineController extends BarController { + static id = "timeline"; + + static defaults = { + dataElementType: "textbar", + dataElementOptions: ["text", "textColor", "textPadding"], + elements: { + showText: true, + textPadding: 4, + minBarWidth: 1, + }, + + layout: { + padding: { + left: 0, + right: 0, + top: 0, + bottom: 0, + }, + }, + }; + + static overrides = { + maintainAspectRatio: false, + plugins: { + legend: { + display: false, + }, + }, + }; + + parseObjectData(meta, data, start, count) { + const iScale = meta.iScale; + const vScale = meta.vScale; + const labels = iScale.getLabels(); + const singleScale = iScale === vScale; + const parsed: any[] = []; + let i; + let ilen; + let item; + let entry; + + for (i = start, ilen = start + count; i < ilen; ++i) { + entry = data[i]; + item = {}; + item[iScale.axis] = singleScale || iScale.parse(labels[i], i); + parsed.push(parseValue(entry, item, vScale, i)); + } + return parsed; + } + + getLabelAndValue(index) { + const meta = this._cachedMeta; + const { vScale } = meta; + const data = this.getDataset().data[index] as TimeLineData; + + return { + label: vScale!.getLabelForValue(this.index) || "", + value: data.label || "", + }; + } + + updateElements( + bars: BarElement[], + start: number, + count: number, + mode: "reset" | "resize" | "none" | "hide" | "show" | "normal" | "active" + ) { + const vScale = this._cachedMeta.vScale!; + const iScale = this._cachedMeta.iScale!; + const dataset = this.getDataset(); + + const firstOpts = this.resolveDataElementOptions(start, mode); + const sharedOptions = this.getSharedOptions(firstOpts); + const includeOptions = this.includeOptions(mode, sharedOptions!); + + const horizontal = vScale.isHorizontal(); + + this.updateSharedOptions(sharedOptions!, mode, firstOpts); + + for (let index = start; index < start + count; index++) { + const data = dataset.data[index] as TimeLineData; + + // @ts-ignore + const y = vScale.getPixelForValue(this.index); + + // @ts-ignore + const xStart = iScale.getPixelForValue(data.start.getTime()); + // @ts-ignore + const xEnd = iScale.getPixelForValue(data.end.getTime()); + const width = xEnd - xStart; + + const height = 10; + + const properties: TextBarProps = { + horizontal, + x: xStart + width / 2, // Center of the bar + y: y - height, // Top of bar + width, + height: 0, + base: y + height, // Bottom of bar, + // Text + text: data.label, + }; + + if (includeOptions) { + properties.options = + sharedOptions || this.resolveDataElementOptions(index, mode); + + properties.options = { + ...properties.options, + backgroundColor: data.color, + }; + } + + this.updateElement(bars[index], index, properties as any, mode); + } + } + + removeHoverStyle(_element, _datasetIndex, _index) { + // this._setStyle(element, index, 'active', false); + } + + setHoverStyle(_element, _datasetIndex, _index) { + // this._setStyle(element, index, 'active', true); + } +} diff --git a/src/components/chart/timeline-chart/timeline-scale.ts b/src/components/chart/timeline-chart/timeline-scale.ts new file mode 100644 index 0000000000..8d5086dafc --- /dev/null +++ b/src/components/chart/timeline-chart/timeline-scale.ts @@ -0,0 +1,55 @@ +import { TimeScale } from "chart.js"; +import { TimeLineData } from "./const"; + +export class TimeLineScale extends TimeScale { + static id = "timeline"; + + static defaults = { + position: "bottom", + tooltips: { + mode: "nearest", + }, + ticks: { + autoSkip: true, + }, + }; + + determineDataLimits() { + const options = this.options; + // @ts-ignore + const adapter = this._adapter; + const unit = options.time.unit || "day"; + let { min, max } = this.getUserBounds(); + + const chart = this.chart; + + // Convert data to timestamps + chart.data.datasets.forEach((dataset, index) => { + if (!chart.isDatasetVisible(index)) { + return; + } + for (const data of dataset.data as TimeLineData[]) { + let timestamp0 = adapter.parse(data.start, this); + let timestamp1 = adapter.parse(data.end, this); + if (timestamp0 > timestamp1) { + [timestamp0, timestamp1] = [timestamp1, timestamp0]; + } + if (min > timestamp0 && timestamp0) { + min = timestamp0; + } + if (max < timestamp1 && timestamp1) { + max = timestamp1; + } + } + }); + + // In case there is no valid min/max, var's use today limits + min = + isFinite(min) && !isNaN(min) ? min : +adapter.startOf(Date.now(), unit); + max = isFinite(max) && !isNaN(max) ? max : +adapter.endOf(Date.now(), unit); + + // Make sure that max is strictly higher than min (required by the lookup table) + this.min = Math.min(min, max - 1); + this.max = Math.max(min + 1, max); + } +} diff --git a/src/components/entity/ha-chart-base.js b/src/components/entity/ha-chart-base.js deleted file mode 100644 index 2ae4d7cf26..0000000000 --- a/src/components/entity/ha-chart-base.js +++ /dev/null @@ -1,661 +0,0 @@ -/* eslint-plugin-disable lit */ -import { IronResizableBehavior } from "@polymer/iron-resizable-behavior/iron-resizable-behavior"; -import { mixinBehaviors } from "@polymer/polymer/lib/legacy/class"; -import { timeOut } from "@polymer/polymer/lib/utils/async"; -import { Debouncer } from "@polymer/polymer/lib/utils/debounce"; -import { html } from "@polymer/polymer/lib/utils/html-tag"; -import { PolymerElement } from "@polymer/polymer/polymer-element"; -import { formatTime } from "../../common/datetime/format_time"; -import "../ha-icon-button"; - -// eslint-disable-next-line no-unused-vars -/* global Chart moment Color */ - -let scriptsLoaded = null; - -class HaChartBase extends mixinBehaviors( - [IronResizableBehavior], - PolymerElement -) { - static get template() { - return html` - - -
- -
-
[[tooltip.title]]
- -
-
    - -
-
-
-
- `; - } - - get chart() { - return this._chart; - } - - static get properties() { - return { - data: Object, - identifier: String, - rendered: { - type: Boolean, - notify: true, - value: false, - readOnly: true, - }, - metas: { - type: Array, - value: () => [], - }, - tooltip: { - type: Object, - value: () => ({ - opacity: "0", - left: "0", - top: "0", - xPadding: "5", - yPadding: "3", - }), - }, - unit: Object, - rtl: { - type: Boolean, - reflectToAttribute: true, - }, - }; - } - - static get observers() { - return ["onPropsChange(data)"]; - } - - connectedCallback() { - super.connectedCallback(); - this._isAttached = true; - this.onPropsChange(); - this._resizeListener = () => { - this._debouncer = Debouncer.debounce( - this._debouncer, - timeOut.after(10), - () => { - if (this._isAttached) { - this.resizeChart(); - } - } - ); - }; - - if (typeof ResizeObserver === "function") { - this.resizeObserver = new ResizeObserver((entries) => { - entries.forEach(() => { - this._resizeListener(); - }); - }); - this.resizeObserver.observe(this.$.chartTarget); - } else { - this.addEventListener("iron-resize", this._resizeListener); - } - - if (scriptsLoaded === null) { - scriptsLoaded = import("../../resources/ha-chart-scripts.js"); - } - scriptsLoaded.then((ChartModule) => { - this.ChartClass = ChartModule.default; - this.onPropsChange(); - }); - } - - disconnectedCallback() { - super.disconnectedCallback(); - this._isAttached = false; - if (this.resizeObserver) { - this.resizeObserver.unobserve(this.$.chartTarget); - } - - this.removeEventListener("iron-resize", this._resizeListener); - - if (this._resizeTimer !== undefined) { - clearInterval(this._resizeTimer); - this._resizeTimer = undefined; - } - } - - onPropsChange() { - if (!this._isAttached || !this.ChartClass || !this.data) { - return; - } - this.drawChart(); - } - - _customTooltips(tooltip) { - // Hide if no tooltip - if (tooltip.opacity === 0) { - this.set(["tooltip", "opacity"], 0); - return; - } - // Set caret Position - if (tooltip.yAlign) { - this.set(["tooltip", "yAlign"], tooltip.yAlign); - } else { - this.set(["tooltip", "yAlign"], "no-transform"); - } - - const title = tooltip.title ? tooltip.title[0] || "" : ""; - this.set(["tooltip", "title"], title); - - if (tooltip.beforeBody) { - this.set(["tooltip", "beforeBody"], tooltip.beforeBody.join("\n")); - } - - const bodyLines = tooltip.body.map((n) => n.lines); - - // Set Text - if (tooltip.body) { - this.set( - ["tooltip", "lines"], - bodyLines.map((body, i) => { - const colors = tooltip.labelColors[i]; - return { - color: colors.borderColor, - bgColor: colors.backgroundColor, - text: body.join("\n"), - }; - }) - ); - } - const parentWidth = this.$.chartTarget.clientWidth; - let positionX = tooltip.caretX; - const positionY = this._chart.canvas.offsetTop + tooltip.caretY; - if (tooltip.caretX + 100 > parentWidth) { - positionX = parentWidth - 100; - } else if (tooltip.caretX < 100) { - positionX = 100; - } - positionX += this._chart.canvas.offsetLeft; - // Display, position, and set styles for font - this.tooltip = { - ...this.tooltip, - opacity: 1, - left: `${positionX}px`, - top: `${positionY}px`, - }; - } - - _legendClick(event) { - event = event || window.event; - event.stopPropagation(); - let target = event.target || event.srcElement; - while (target.nodeName !== "LI") { - // user clicked child, find parent LI - target = target.parentElement; - } - const index = event.model.itemsIndex; - - const meta = this._chart.getDatasetMeta(index); - meta.hidden = - meta.hidden === null ? !this._chart.data.datasets[index].hidden : null; - this.set( - ["metas", index, "hidden"], - this._chart.isDatasetVisible(index) ? null : "hidden" - ); - this._chart.update(); - } - - _drawLegend() { - const chart = this._chart; - // New data for old graph. Keep metadata. - const preserveVisibility = - this._oldIdentifier && this.identifier === this._oldIdentifier; - this._oldIdentifier = this.identifier; - this.set( - "metas", - this._chart.data.datasets.map((x, i) => ({ - label: x.label, - color: x.color, - bgColor: x.backgroundColor, - hidden: - preserveVisibility && i < this.metas.length - ? this.metas[i].hidden - : !chart.isDatasetVisible(i), - })) - ); - let updateNeeded = false; - if (preserveVisibility) { - for (let i = 0; i < this.metas.length; i++) { - const meta = chart.getDatasetMeta(i); - if (!!meta.hidden !== !!this.metas[i].hidden) updateNeeded = true; - meta.hidden = this.metas[i].hidden ? true : null; - } - } - if (updateNeeded) { - chart.update(); - } - this.unit = this.data.unit; - } - - _formatTickValue(value, index, values) { - if (values.length === 0) { - return value; - } - const date = new Date(values[index].value); - return formatTime(date, this.hass.locale); - } - - drawChart() { - const data = this.data.data; - const ctx = this.$.chartCanvas; - - if ((!data.datasets || !data.datasets.length) && !this._chart) { - return; - } - if (this.data.type !== "timeline" && data.datasets.length > 0) { - const cnt = data.datasets.length; - const colors = this.constructor.getColorList(cnt); - for (let loopI = 0; loopI < cnt; loopI++) { - data.datasets[loopI].borderColor = colors[loopI].rgbString(); - data.datasets[loopI].backgroundColor = colors[loopI] - .alpha(0.6) - .rgbaString(); - } - } - - if (this._chart) { - this._customTooltips({ opacity: 0 }); - this._chart.data = data; - this._chart.update({ duration: 0 }); - if (this.isTimeline) { - this._chart.options.scales.yAxes[0].gridLines.display = data.length > 1; - } else if (this.data.legend === true) { - this._drawLegend(); - } - this.resizeChart(); - } else { - if (!data.datasets) { - return; - } - this._customTooltips({ opacity: 0 }); - const plugins = [{ afterRender: () => this._setRendered(true) }]; - let options = { - responsive: true, - maintainAspectRatio: false, - animation: { - duration: 0, - }, - hover: { - animationDuration: 0, - }, - responsiveAnimationDuration: 0, - tooltips: { - enabled: false, - custom: this._customTooltips.bind(this), - }, - legend: { - display: false, - }, - line: { - spanGaps: true, - }, - elements: { - font: "12px 'Roboto', 'sans-serif'", - }, - ticks: { - fontFamily: "'Roboto', 'sans-serif'", - }, - }; - options = Chart.helpers.merge(options, this.data.options); - options.scales.xAxes[0].ticks.callback = this._formatTickValue.bind(this); - if (this.data.type === "timeline") { - this.set("isTimeline", true); - if (this.data.colors !== undefined) { - this._colorFunc = this.constructor.getColorGenerator( - this.data.colors.staticColors, - this.data.colors.staticColorIndex - ); - } - if (this._colorFunc !== undefined) { - options.elements.colorFunction = this._colorFunc; - } - if (data.datasets.length === 1) { - if (options.scales.yAxes[0].ticks) { - options.scales.yAxes[0].ticks.display = false; - } else { - options.scales.yAxes[0].ticks = { display: false }; - } - if (options.scales.yAxes[0].gridLines) { - options.scales.yAxes[0].gridLines.display = false; - } else { - options.scales.yAxes[0].gridLines = { display: false }; - } - } - this.$.chartTarget.style.height = "50px"; - } else { - this.$.chartTarget.style.height = "160px"; - } - const chartData = { - type: this.data.type, - data: this.data.data, - options: options, - plugins: plugins, - }; - // Async resize after dom update - this._chart = new this.ChartClass(ctx, chartData); - if (this.isTimeline !== true && this.data.legend === true) { - this._drawLegend(); - } - this.resizeChart(); - } - } - - resizeChart() { - if (!this._chart) return; - // Chart not ready - if (this._resizeTimer === undefined) { - this._resizeTimer = setInterval(this.resizeChart.bind(this), 10); - return; - } - - clearInterval(this._resizeTimer); - this._resizeTimer = undefined; - - this._resizeChart(); - } - - _resizeChart() { - const chartTarget = this.$.chartTarget; - - const options = this.data; - const data = options.data; - - if (data.datasets.length === 0) { - return; - } - - if (!this.isTimeline) { - this._chart.resize(); - return; - } - - // Recalculate chart height for Timeline chart - const areaTop = this._chart.chartArea.top; - const areaBot = this._chart.chartArea.bottom; - const height1 = this._chart.canvas.clientHeight; - if (areaBot > 0) { - this._axisHeight = height1 - areaBot + areaTop; - } - - if (!this._axisHeight) { - chartTarget.style.height = "50px"; - this._chart.resize(); - this.resizeChart(); - return; - } - if (this._axisHeight) { - const cnt = data.datasets.length; - const targetHeight = 30 * cnt + this._axisHeight + "px"; - if (chartTarget.style.height !== targetHeight) { - chartTarget.style.height = targetHeight; - } - this._chart.resize(); - } - } - - // Get HSL distributed color list - static getColorList(count) { - let processL = false; - if (count > 10) { - processL = true; - count = Math.ceil(count / 2); - } - const h1 = 360 / count; - const result = []; - for (let loopI = 0; loopI < count; loopI++) { - result[loopI] = Color().hsl(h1 * loopI, 80, 38); - if (processL) { - result[loopI + count] = Color().hsl(h1 * loopI, 80, 62); - } - } - return result; - } - - static getColorGenerator(staticColors, startIndex) { - // Known colors for static data, - // should add for very common state string manually. - // Palette modified from http://google.github.io/palette.js/ mpn65, Apache 2.0 - const palette = [ - "ff0029", - "66a61e", - "377eb8", - "984ea3", - "00d2d5", - "ff7f00", - "af8d00", - "7f80cd", - "b3e900", - "c42e60", - "a65628", - "f781bf", - "8dd3c7", - "bebada", - "fb8072", - "80b1d3", - "fdb462", - "fccde5", - "bc80bd", - "ffed6f", - "c4eaff", - "cf8c00", - "1b9e77", - "d95f02", - "e7298a", - "e6ab02", - "a6761d", - "0097ff", - "00d067", - "f43600", - "4ba93b", - "5779bb", - "927acc", - "97ee3f", - "bf3947", - "9f5b00", - "f48758", - "8caed6", - "f2b94f", - "eff26e", - "e43872", - "d9b100", - "9d7a00", - "698cff", - "d9d9d9", - "00d27e", - "d06800", - "009f82", - "c49200", - "cbe8ff", - "fecddf", - "c27eb6", - "8cd2ce", - "c4b8d9", - "f883b0", - "a49100", - "f48800", - "27d0df", - "a04a9b", - ]; - function getColorIndex(idx) { - // Reuse the color if index too large. - return Color("#" + palette[idx % palette.length]); - } - const colorDict = {}; - let colorIndex = 0; - if (startIndex > 0) colorIndex = startIndex; - if (staticColors) { - Object.keys(staticColors).forEach((c) => { - const c1 = staticColors[c]; - if (isFinite(c1)) { - colorDict[c.toLowerCase()] = getColorIndex(c1); - } else { - colorDict[c.toLowerCase()] = Color(staticColors[c]); - } - }); - } - // Custom color assign - function getColor(__, data) { - let ret; - const name = data[3]; - if (name === null) return Color().hsl(0, 40, 38); - if (name === undefined) return Color().hsl(120, 40, 38); - let name1 = name.toLowerCase(); - if (ret === undefined) { - if (data[4]) { - // Invert on/off if data[4] is true. Required for some binary_sensor device classes - // (BINARY_SENSOR_DEVICE_CLASS_COLOR_INVERTED) where "off" is the good (= green color) value. - name1 = name1 === "on" ? "off" : name1 === "off" ? "on" : name1; - } - - ret = colorDict[name1]; - } - if (ret === undefined) { - ret = getColorIndex(colorIndex); - colorIndex++; - colorDict[name1] = ret; - } - return ret; - } - return getColor; - } -} -customElements.define("ha-chart-base", HaChartBase); diff --git a/src/components/state-history-chart-line.js b/src/components/state-history-chart-line.js deleted file mode 100644 index dc767fb45c..0000000000 --- a/src/components/state-history-chart-line.js +++ /dev/null @@ -1,433 +0,0 @@ -import "@polymer/polymer/lib/utils/debounce"; -import { html } from "@polymer/polymer/lib/utils/html-tag"; -/* eslint-plugin-disable lit */ -import { PolymerElement } from "@polymer/polymer/polymer-element"; -import { formatDateTimeWithSeconds } from "../common/datetime/format_date_time"; -import LocalizeMixin from "../mixins/localize-mixin"; -import "./entity/ha-chart-base"; - -class StateHistoryChartLine extends LocalizeMixin(PolymerElement) { - static get template() { - return html` - - - `; - } - - static get properties() { - return { - hass: { - type: Object, - }, - chartData: Object, - data: Object, - names: Object, - unit: String, - identifier: String, - - isSingleDevice: { - type: Boolean, - value: false, - }, - - endTime: Object, - rendered: { - type: Boolean, - value: false, - observer: "_onRenderedChanged", - }, - }; - } - - static get observers() { - return ["dataChanged(data, endTime, isSingleDevice)"]; - } - - connectedCallback() { - super.connectedCallback(); - this._isAttached = true; - this.drawChart(); - } - - ready() { - super.ready(); - // safari doesn't always render the canvas when we animate it, so we remove overflow hidden when the animation is complete - this.addEventListener("transitionend", () => { - this.style.overflow = "auto"; - }); - } - - dataChanged() { - this.drawChart(); - } - - _onRenderedChanged(rendered) { - if (rendered) { - this.animateHeight(); - } - } - - animateHeight() { - requestAnimationFrame(() => - requestAnimationFrame(() => { - this.style.height = this.$.chart.scrollHeight + "px"; - }) - ); - } - - drawChart() { - if (!this._isAttached) { - return; - } - - const unit = this.unit; - const deviceStates = this.data; - const datasets = []; - let endTime; - - if (deviceStates.length === 0) { - return; - } - - function safeParseFloat(value) { - const parsed = parseFloat(value); - return isFinite(parsed) ? parsed : null; - } - - endTime = - this.endTime || - // Get the highest date from the last date of each device - new Date( - Math.max.apply( - null, - deviceStates.map( - (devSts) => - new Date(devSts.states[devSts.states.length - 1].last_changed) - ) - ) - ); - if (endTime > new Date()) { - endTime = new Date(); - } - - const names = this.names || {}; - deviceStates.forEach((states) => { - const domain = states.domain; - const name = names[states.entity_id] || states.name; - // array containing [value1, value2, etc] - let prevValues; - const data = []; - - function pushData(timestamp, datavalues) { - if (!datavalues) return; - if (timestamp > endTime) { - // Drop datapoints that are after the requested endTime. This could happen if - // endTime is "now" and client time is not in sync with server time. - return; - } - data.forEach((d, i) => { - if (datavalues[i] === null && prevValues && prevValues[i] !== null) { - // null data values show up as gaps in the chart. - // If the current value for the dataset is null and the previous - // value of the data set is not null, then add an 'end' point - // to the chart for the previous value. Otherwise the gap will - // be too big. It will go from the start of the previous data - // value until the start of the next data value. - d.data.push({ x: timestamp, y: prevValues[i] }); - } - d.data.push({ x: timestamp, y: datavalues[i] }); - }); - prevValues = datavalues; - } - - function addColumn(nameY, step, fill) { - let dataFill = false; - let dataStep = false; - if (fill) { - dataFill = "origin"; - } - if (step) { - dataStep = "before"; - } - data.push({ - label: nameY, - fill: dataFill, - steppedLine: dataStep, - pointRadius: 0, - data: [], - unitText: unit, - }); - } - - if ( - domain === "thermostat" || - domain === "climate" || - domain === "water_heater" - ) { - const hasHvacAction = states.states.some( - (state) => state.attributes && state.attributes.hvac_action - ); - - const isHeating = - domain === "climate" && hasHvacAction - ? (state) => state.attributes.hvac_action === "heating" - : (state) => state.state === "heat"; - const isCooling = - domain === "climate" && hasHvacAction - ? (state) => state.attributes.hvac_action === "cooling" - : (state) => state.state === "cool"; - - const hasHeat = states.states.some(isHeating); - const hasCool = states.states.some(isCooling); - // We differentiate between thermostats that have a target temperature - // range versus ones that have just a target temperature - - // Using step chart by step-before so manually interpolation not needed. - const hasTargetRange = states.states.some( - (state) => - state.attributes && - state.attributes.target_temp_high !== - state.attributes.target_temp_low - ); - - addColumn( - `${this.hass.localize( - "ui.card.climate.current_temperature", - "name", - name - )}`, - true - ); - if (hasHeat) { - addColumn( - `${this.hass.localize("ui.card.climate.heating", "name", name)}`, - true, - true - ); - // The "heating" series uses steppedArea to shade the area below the current - // temperature when the thermostat is calling for heat. - } - if (hasCool) { - addColumn( - `${this.hass.localize("ui.card.climate.cooling", "name", name)}`, - true, - true - ); - // The "cooling" series uses steppedArea to shade the area below the current - // temperature when the thermostat is calling for heat. - } - - if (hasTargetRange) { - addColumn( - `${this.hass.localize( - "ui.card.climate.target_temperature_mode", - "name", - name, - "mode", - this.hass.localize("ui.card.climate.high") - )}`, - true - ); - addColumn( - `${this.hass.localize( - "ui.card.climate.target_temperature_mode", - "name", - name, - "mode", - this.hass.localize("ui.card.climate.low") - )}`, - true - ); - } else { - addColumn( - `${this.hass.localize( - "ui.card.climate.target_temperature_entity", - "name", - name - )}`, - true - ); - } - - states.states.forEach((state) => { - if (!state.attributes) return; - const curTemp = safeParseFloat(state.attributes.current_temperature); - const series = [curTemp]; - if (hasHeat) { - series.push(isHeating(state) ? curTemp : null); - } - if (hasCool) { - series.push(isCooling(state) ? curTemp : null); - } - if (hasTargetRange) { - const targetHigh = safeParseFloat( - state.attributes.target_temp_high - ); - const targetLow = safeParseFloat(state.attributes.target_temp_low); - series.push(targetHigh, targetLow); - pushData(new Date(state.last_changed), series); - } else { - const target = safeParseFloat(state.attributes.temperature); - series.push(target); - pushData(new Date(state.last_changed), series); - } - }); - } else if (domain === "humidifier") { - addColumn( - `${this.hass.localize( - "ui.card.humidifier.target_humidity_entity", - "name", - name - )}`, - true - ); - addColumn( - `${this.hass.localize("ui.card.humidifier.on_entity", "name", name)}`, - true, - true - ); - - states.states.forEach((state) => { - if (!state.attributes) return; - const target = safeParseFloat(state.attributes.humidity); - const series = [target]; - series.push(state.state === "on" ? target : null); - pushData(new Date(state.last_changed), series); - }); - } else { - // Only disable interpolation for sensors - const isStep = domain === "sensor"; - addColumn(name, isStep); - - let lastValue = null; - let lastDate = null; - let lastNullDate = null; - - // Process chart data. - // When state is `unknown`, calculate the value and break the line. - states.states.forEach((state) => { - const value = safeParseFloat(state.state); - const date = new Date(state.last_changed); - if (value !== null && lastNullDate !== null) { - const dateTime = date.getTime(); - const lastNullDateTime = lastNullDate.getTime(); - const lastDateTime = lastDate.getTime(); - const tmpValue = - (value - lastValue) * - ((lastNullDateTime - lastDateTime) / - (dateTime - lastDateTime)) + - lastValue; - pushData(lastNullDate, [tmpValue]); - pushData(new Date(lastNullDateTime + 1), [null]); - pushData(date, [value]); - lastDate = date; - lastValue = value; - lastNullDate = null; - } else if (value !== null && lastNullDate === null) { - pushData(date, [value]); - lastDate = date; - lastValue = value; - } else if ( - value === null && - lastNullDate === null && - lastValue !== null - ) { - lastNullDate = date; - } - }); - } - - // Add an entry for final values - pushData(endTime, prevValues, false); - - // Concat two arrays - Array.prototype.push.apply(datasets, data); - }); - - const formatTooltipTitle = (items, data) => { - const item = items[0]; - const date = data.datasets[item.datasetIndex].data[item.index].x; - - return formatDateTimeWithSeconds(date, this.hass.locale); - }; - - const chartOptions = { - type: "line", - unit: unit, - legend: !this.isSingleDevice, - options: { - scales: { - xAxes: [ - { - type: "time", - ticks: { - major: { - fontStyle: "bold", - }, - source: "auto", - sampleSize: 5, - autoSkipPadding: 20, - maxRotation: 0, - }, - }, - ], - yAxes: [ - { - ticks: { - maxTicksLimit: 7, - }, - }, - ], - }, - tooltips: { - mode: "neareach", - callbacks: { - title: formatTooltipTitle, - }, - }, - hover: { - mode: "neareach", - }, - layout: { - padding: { - top: 5, - }, - }, - elements: { - line: { - tension: 0.1, - pointRadius: 0, - borderWidth: 1.5, - }, - point: { - hitRadius: 5, - }, - }, - plugins: { - filler: { - propagate: true, - }, - }, - }, - data: { - labels: [], - datasets: datasets, - }, - }; - this.chartData = chartOptions; - } -} -customElements.define("state-history-chart-line", StateHistoryChartLine); diff --git a/src/components/state-history-chart-timeline.js b/src/components/state-history-chart-timeline.js deleted file mode 100644 index fa29388da3..0000000000 --- a/src/components/state-history-chart-timeline.js +++ /dev/null @@ -1,286 +0,0 @@ -import "@polymer/polymer/lib/utils/debounce"; -import { html } from "@polymer/polymer/lib/utils/html-tag"; -/* eslint-plugin-disable lit */ -import { PolymerElement } from "@polymer/polymer/polymer-element"; -import { formatDateTimeWithSeconds } from "../common/datetime/format_date_time"; -import { computeDomain } from "../common/entity/compute_domain"; -import { computeRTL } from "../common/util/compute_rtl"; -import LocalizeMixin from "../mixins/localize-mixin"; -import "./entity/ha-chart-base"; - -/** Binary sensor device classes for which the static colors for on/off need to be inverted. - * List the ones were "off" = good or normal state = should be rendered "green". - */ -const BINARY_SENSOR_DEVICE_CLASS_COLOR_INVERTED = new Set([ - "battery", - "door", - "garage_door", - "gas", - "lock", - "opening", - "problem", - "safety", - "smoke", - "window", -]); - -class StateHistoryChartTimeline extends LocalizeMixin(PolymerElement) { - static get template() { - return html` - - - `; - } - - static get properties() { - return { - hass: { - type: Object, - }, - chartData: Object, - data: { - type: Object, - observer: "dataChanged", - }, - names: Object, - noSingle: Boolean, - endTime: Date, - rendered: { - type: Boolean, - value: false, - reflectToAttribute: true, - }, - rtl: { - reflectToAttribute: true, - computed: "_computeRTL(hass)", - }, - }; - } - - static get observers() { - return ["dataChanged(data, endTime, localize, language)"]; - } - - connectedCallback() { - super.connectedCallback(); - this._isAttached = true; - this.drawChart(); - } - - dataChanged() { - this.drawChart(); - } - - drawChart() { - const staticColors = { - on: 1, - off: 0, - home: 1, - not_home: 0, - unavailable: "#a0a0a0", - unknown: "#606060", - idle: 2, - }; - let stateHistory = this.data; - - if (!this._isAttached) { - return; - } - - if (!stateHistory) { - stateHistory = []; - } - - const startTime = new Date( - stateHistory.reduce( - (minTime, stateInfo) => - Math.min(minTime, new Date(stateInfo.data[0].last_changed)), - new Date() - ) - ); - - // end time is Math.max(startTime, last_event) - let endTime = - this.endTime || - new Date( - stateHistory.reduce( - (maxTime, stateInfo) => - Math.max( - maxTime, - new Date(stateInfo.data[stateInfo.data.length - 1].last_changed) - ), - startTime - ) - ); - - if (endTime > new Date()) { - endTime = new Date(); - } - - const labels = []; - const datasets = []; - // stateHistory is a list of lists of sorted state objects - const names = this.names || {}; - stateHistory.forEach((stateInfo) => { - let newLastChanged; - let prevState = null; - let locState = null; - let prevLastChanged = startTime; - const entityDisplay = names[stateInfo.entity_id] || stateInfo.name; - - const invertOnOff = - computeDomain(stateInfo.entity_id) === "binary_sensor" && - BINARY_SENSOR_DEVICE_CLASS_COLOR_INVERTED.has( - this.hass.states[stateInfo.entity_id].attributes.device_class - ); - - const dataRow = []; - stateInfo.data.forEach((state) => { - let newState = state.state; - const timeStamp = new Date(state.last_changed); - if (newState === undefined || newState === "") { - newState = null; - } - if (timeStamp > endTime) { - // Drop datapoints that are after the requested endTime. This could happen if - // endTime is 'now' and client time is not in sync with server time. - return; - } - if (prevState !== null && newState !== prevState) { - newLastChanged = new Date(state.last_changed); - - dataRow.push([ - prevLastChanged, - newLastChanged, - locState, - prevState, - invertOnOff, - ]); - - prevState = newState; - locState = state.state_localize; - prevLastChanged = newLastChanged; - } else if (prevState === null) { - prevState = newState; - locState = state.state_localize; - prevLastChanged = new Date(state.last_changed); - } - }); - - if (prevState !== null) { - dataRow.push([ - prevLastChanged, - endTime, - locState, - prevState, - invertOnOff, - ]); - } - datasets.push({ - data: dataRow, - entity_id: stateInfo.entity_id, - }); - labels.push(entityDisplay); - }); - - const formatTooltipLabel = (item, data) => { - const values = data.datasets[item.datasetIndex].data[item.index]; - - const start = formatDateTimeWithSeconds(values[0], this.hass.locale); - const end = formatDateTimeWithSeconds(values[1], this.hass.locale); - const state = values[2]; - - return [state, start, end]; - }; - - const formatTooltipBeforeBody = (item, data) => { - if (!this.hass.userData || !this.hass.userData.showAdvanced || !item[0]) { - return ""; - } - // Extract the entity ID from the dataset. - const values = data.datasets[item[0].datasetIndex]; - return values.entity_id || ""; - }; - - const chartOptions = { - type: "timeline", - options: { - tooltips: { - callbacks: { - label: formatTooltipLabel, - beforeBody: formatTooltipBeforeBody, - }, - }, - scales: { - xAxes: [ - { - ticks: { - major: { - fontStyle: "bold", - }, - sampleSize: 5, - autoSkipPadding: 50, - maxRotation: 0, - }, - categoryPercentage: undefined, - barPercentage: undefined, - time: { - format: undefined, - }, - }, - ], - yAxes: [ - { - afterSetDimensions: (yaxe) => { - yaxe.maxWidth = yaxe.chart.width * 0.18; - }, - position: this._computeRTL ? "right" : "left", - categoryPercentage: undefined, - barPercentage: undefined, - time: { format: undefined }, - }, - ], - }, - }, - datasets: { - categoryPercentage: 0.8, - barPercentage: 0.9, - }, - data: { - labels: labels, - datasets: datasets, - }, - colors: { - staticColors: staticColors, - staticColorIndex: 3, - }, - }; - this.chartData = chartOptions; - } - - _computeRTL(hass) { - return computeRTL(hass); - } -} -customElements.define( - "state-history-chart-timeline", - StateHistoryChartTimeline -); diff --git a/src/dialogs/more-info/ha-more-info-history.ts b/src/dialogs/more-info/ha-more-info-history.ts index 6dcb3144de..199e8e9490 100644 --- a/src/dialogs/more-info/ha-more-info-history.ts +++ b/src/dialogs/more-info/ha-more-info-history.ts @@ -2,7 +2,7 @@ import { html, LitElement, PropertyValues, TemplateResult } from "lit"; import { customElement, property, state } from "lit/decorators"; import { isComponentLoaded } from "../../common/config/is_component_loaded"; import { throttle } from "../../common/util/throttle"; -import "../../components/state-history-charts"; +import "../../components/chart/state-history-charts"; import { getRecentWithCache } from "../../data/cached-history"; import { HistoryResult } from "../../data/history"; import { HomeAssistant } from "../../types"; diff --git a/src/dialogs/more-info/ha-more-info-logbook.ts b/src/dialogs/more-info/ha-more-info-logbook.ts index 90d01e75a9..f109633e53 100644 --- a/src/dialogs/more-info/ha-more-info-logbook.ts +++ b/src/dialogs/more-info/ha-more-info-logbook.ts @@ -4,7 +4,6 @@ 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 { fetchUsers } from "../../data/user"; import { getLogbookData, LogbookEntry } from "../../data/logbook"; import { loadTraceContexts, TraceContexts } from "../../data/trace"; diff --git a/src/panels/history/ha-panel-history.ts b/src/panels/history/ha-panel-history.ts index 31737c8874..cef5aa53c3 100644 --- a/src/panels/history/ha-panel-history.ts +++ b/src/panels/history/ha-panel-history.ts @@ -8,7 +8,7 @@ import "../../components/ha-circular-progress"; import "../../components/ha-date-range-picker"; import type { DateRangePickerRanges } from "../../components/ha-date-range-picker"; import "../../components/ha-menu-button"; -import "../../components/state-history-charts"; +import "../../components/chart/state-history-charts"; import { computeHistory, fetchDate } from "../../data/history"; import "../../layouts/ha-app-layout"; import { haStyle } from "../../resources/styles"; diff --git a/src/panels/lovelace/cards/hui-history-graph-card.ts b/src/panels/lovelace/cards/hui-history-graph-card.ts index b17a1ad1e0..b61f3c0512 100644 --- a/src/panels/lovelace/cards/hui-history-graph-card.ts +++ b/src/panels/lovelace/cards/hui-history-graph-card.ts @@ -10,7 +10,7 @@ import { customElement, property, state } from "lit/decorators"; import { classMap } from "lit/directives/class-map"; import { throttle } from "../../../common/util/throttle"; import "../../../components/ha-card"; -import "../../../components/state-history-charts"; +import "../../../components/chart/state-history-charts"; import { CacheConfig, getRecentWithCache } from "../../../data/cached-history"; import { HistoryResult } from "../../../data/history"; import { HomeAssistant } from "../../../types"; @@ -139,8 +139,8 @@ export class HuiHistoryGraphCard extends LitElement implements LovelaceCard { .isLoadingData=${!this._stateHistory} .historyData=${this._stateHistory} .names=${this._names} - .upToNow=${true} - .noSingle=${true} + up-to-now + no-single >
diff --git a/src/panels/lovelace/cards/hui-map-card.ts b/src/panels/lovelace/cards/hui-map-card.ts index e6c825d9b9..88ccf2104c 100644 --- a/src/panels/lovelace/cards/hui-map-card.ts +++ b/src/panels/lovelace/cards/hui-map-card.ts @@ -25,23 +25,10 @@ import "../../../components/map/ha-map"; import { mdiImageFilterCenterFocus } from "@mdi/js"; import type { HaMap, HaMapPaths } from "../../../components/map/ha-map"; import memoizeOne from "memoize-one"; +import { getColorByIndex } from "../../../common/color/colors"; const MINUTE = 60000; -const COLORS = [ - "#0288D1", - "#00AA00", - "#984ea3", - "#00d2d5", - "#ff7f00", - "#af8d00", - "#7f80cd", - "#b3e900", - "#c42e60", - "#a65628", - "#f781bf", - "#8dd3c7", -]; @customElement("hui-map-card") class HuiMapCard extends LitElement implements LovelaceCard { @property({ attribute: false }) public hass!: HomeAssistant; @@ -225,7 +212,7 @@ class HuiMapCard extends LitElement implements LovelaceCard { if (color) { return color; } - color = COLORS[this._colorIndex % COLORS.length]; + color = getColorByIndex(this._colorIndex); this._colorIndex++; this._colorDict[entityId] = color; return color; diff --git a/src/panels/lovelace/cards/hui-thermostat-card.ts b/src/panels/lovelace/cards/hui-thermostat-card.ts index 1d50d04180..19ba06708a 100644 --- a/src/panels/lovelace/cards/hui-thermostat-card.ts +++ b/src/panels/lovelace/cards/hui-thermostat-card.ts @@ -447,47 +447,37 @@ export class HuiThermostatCard extends LitElement implements LovelaceCard { --name-font-size: 1.2rem; --brightness-font-size: 1.2rem; --rail-border-color: transparent; - --auto-color: green; - --eco-color: springgreen; - --cool-color: #2b9af9; - --heat-color: #ff8100; - --manual-color: #44739e; - --off-color: #8a8a8a; - --fan_only-color: #8a8a8a; - --dry-color: #efbd07; - --idle-color: #8a8a8a; - --unknown-color: #bac; } .auto, .heat_cool { - --mode-color: var(--auto-color); + --mode-color: var(--state-climate-auto-color); } .cool { - --mode-color: var(--cool-color); + --mode-color: var(--state-climate-cool-color); } .heat { - --mode-color: var(--heat-color); + --mode-color: var(--state-climate-heat-color); } .manual { - --mode-color: var(--manual-color); + --mode-color: var(--state-climate-manual-color); } .off { - --mode-color: var(--off-color); + --mode-color: var(--state-climate-off-color); } .fan_only { - --mode-color: var(--fan_only-color); + --mode-color: var(--state-climate-fan_only-color); } .eco { - --mode-color: var(--eco-color); + --mode-color: var(--state-climate-eco-color); } .dry { - --mode-color: var(--dry-color); + --mode-color: var(--state-climate-dry-color); } .idle { - --mode-color: var(--idle-color); + --mode-color: var(--state-climate-idle-color); } .unknown-mode { - --mode-color: var(--unknown-color); + --mode-color: var(--state-unknown-color); } .more-info { diff --git a/src/resources/chartjs.ts b/src/resources/chartjs.ts new file mode 100644 index 0000000000..811addaa8b --- /dev/null +++ b/src/resources/chartjs.ts @@ -0,0 +1,35 @@ +import { + LineController, + TimeScale, + LinearScale, + PointElement, + LineElement, + Filler, + Legend, + Title, + Tooltip, + CategoryScale, + Chart, +} from "chart.js"; +import { TextBarElement } from "../components/chart/timeline-chart/textbar-element"; +import { TimelineController } from "../components/chart/timeline-chart/timeline-controller"; +import { TimeLineScale } from "../components/chart/timeline-chart/timeline-scale"; +import "../components/chart/chart-date-adapter"; + +export { Chart } from "chart.js"; + +Chart.register( + Tooltip, + Title, + Legend, + Filler, + TimeScale, + LinearScale, + LineController, + PointElement, + LineElement, + TextBarElement, + TimeLineScale, + TimelineController, + CategoryScale +); diff --git a/src/resources/ha-chart-scripts.js b/src/resources/ha-chart-scripts.js deleted file mode 100644 index 500c803788..0000000000 --- a/src/resources/ha-chart-scripts.js +++ /dev/null @@ -1,60 +0,0 @@ -import Chart from "chart.js"; -import "chartjs-chart-timeline"; - -// This function add a new interaction mode to Chart.js that -// returns one point for every dataset. -Chart.Interaction.modes.neareach = function (chart, e, options) { - const getRange = { - x: (a, b) => Math.abs(a.x - b.x), - y: (a, b) => Math.abs(a.y - b.y), - // eslint-disable-next-line no-restricted-properties - xy: (a, b) => Math.pow(a.x - b.x, 2) + Math.pow(a.y - b.y, 2), - }; - const getRangeMax = { - x: (r) => r, - y: (r) => r, - xy: (r) => r * r, - }; - let position; - if (e.native) { - position = { - x: e.x, - y: e.y, - }; - } else { - position = Chart.helpers.getRelativePosition(e, chart); - } - const elements = []; - const elementsRange = []; - const datasets = chart.data.datasets; - let meta; - options.axis = options.axis || "xy"; - const rangeFunc = getRange[options.axis]; - const rangeMaxFunc = getRangeMax[options.axis]; - - for (let i = 0, ilen = datasets.length; i < ilen; ++i) { - if (!chart.isDatasetVisible(i)) { - continue; - } - - meta = chart.getDatasetMeta(i); - for (let j = 0, jlen = meta.data.length; j < jlen; ++j) { - const element = meta.data[j]; - if (!element._view.skip) { - const vm = element._view; - const range = rangeFunc(vm, position); - const oldRange = elementsRange[i]; - if (range < rangeMaxFunc(vm.radius + vm.hitRadius)) { - if (oldRange === undefined || oldRange > range) { - elementsRange[i] = range; - elements[i] = element; - } - } - } - } - } - const ret = elements.filter((n) => n !== undefined); - return ret; -}; - -export default Chart; diff --git a/src/resources/ha-style.ts b/src/resources/ha-style.ts index 82439c105e..fbfeb09eed 100644 --- a/src/resources/ha-style.ts +++ b/src/resources/ha-style.ts @@ -42,10 +42,6 @@ documentContainer.innerHTML = ` --success-color: #0f9d58; --info-color: #4285f4; - /* states and badges */ - --state-icon-color: #44739e; - --state-icon-active-color: #FDD835; - /* background and sidebar */ --card-background-color: #ffffff; --primary-background-color: #fafafa; @@ -60,6 +56,32 @@ documentContainer.innerHTML = ` --label-badge-green: #0DA035; --label-badge-yellow: #f4b400; + /* states and badges */ + --state-icon-color: #44739e; + /* an active state is anything that would require attention */ + --state-icon-active-color: #FDD835; + /* an error state is anything that would be considered an error */ + /* --state-icon-error-color: #db4437; derived from error-color */ + + --state-on-color: #66a61e; + --state-off-color: #ff0029; + --state-home-color: #66a61e; + --state-not_home-color: #ff0029; + /* --state-unavailable-color: #a0a0a0; derived from disabled-text-color */ + --state-unknown-color: #606060; + --state-idle-color: #377eb8; + + /* climate state colors */ + --state-climate-auto-color: #008000; + --state-climate-eco-color: #00ff7f; + --state-climate-cool-color: #2b9af9; + --state-climate-heat-color: #ff8100; + --state-climate-manual-color: #44739e; + --state-climate-off-color: #8a8a8a; + --state-climate-fan_only-color: #8a8a8a; + --state-climate-dry-color: #efbd07; + --state-climate-idle-color: #8a8a8a; + /* Paper-styles color.html dependency is stripped on build. When a default paper-style color is used, it needs to be copied diff --git a/src/resources/styles.ts b/src/resources/styles.ts index 54e298f1c2..035832e689 100644 --- a/src/resources/styles.ts +++ b/src/resources/styles.ts @@ -34,8 +34,9 @@ export const darkStyles = { }; export const derivedStyles = { - "error-state-color": "var(--error-color)", - "state-icon-unavailable-color": "var(--disabled-text-color)", + "state-icon-error-color": "var(--error-state-color, var(--error-color))", + "state-unavailable-color": + "var(--state-icon-unavailable-color, var(--disabled-text-color))", "sidebar-text-color": "var(--primary-text-color)", "sidebar-background-color": "var(--card-background-color)", "sidebar-selected-text-color": "var(--primary-color)", diff --git a/yarn.lock b/yarn.lock index eae3bc00de..1671fdf6e3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4634,33 +4634,10 @@ chardet@^0.7.0: resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== -chart.js@^2.9.4: - version "2.9.4" - resolved "https://registry.yarnpkg.com/chart.js/-/chart.js-2.9.4.tgz#0827f9563faffb2dc5c06562f8eb10337d5b9684" - integrity sha512-B07aAzxcrikjAPyV+01j7BmOpxtQETxTSlQ26BEYJ+3iUkbNKaOJ/nDbT6JjyqYxseM0ON12COHYdU2cTIjC7A== - dependencies: - chartjs-color "^2.1.0" - moment "^2.10.2" - -chartjs-chart-timeline@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/chartjs-chart-timeline/-/chartjs-chart-timeline-0.4.0.tgz#cbd25dc5ddb5c2b34289f8dd7a2a627d71e251e8" - integrity sha512-a3iOFgMUXgEK9zyDFXlL7cfhO6z4DkeuGqok1xnNVNg12ciSt/k1jDBFk8JKN+sVNZfoqeGAFBT9zvb++iEWnA== - -chartjs-color-string@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/chartjs-color-string/-/chartjs-color-string-0.6.0.tgz#1df096621c0e70720a64f4135ea171d051402f71" - integrity sha512-TIB5OKn1hPJvO7JcteW4WY/63v6KwEdt6udfnDE9iCAZgy+V4SrbSxoIbTw/xkUIapjEI4ExGtD0+6D3KyFd7A== - dependencies: - color-name "^1.0.0" - -chartjs-color@^2.1.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/chartjs-color/-/chartjs-color-2.3.0.tgz#0e7e1e8dba37eae8415fd3db38bf572007dd958f" - integrity sha512-hEvVheqczsoHD+fZ+tfPUE+1+RbV6b+eksp2LwAhwRTVXEjCSEavvk+Hg3H6SZfGlPh/UfmWKGIvZbtobOEm3g== - dependencies: - chartjs-color-string "^0.6.0" - color-convert "^0.5.3" +chart.js@^3.3.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/chart.js/-/chart.js-3.3.2.tgz#667f3a0b6371b9719d8949c04a5bcbaec0d8c615" + integrity sha512-H0hSO7xqTIrwxoACqnSoNromEMfXvfuVnrbuSt2TuXfBDDofbnto4zuZlRtRvC73/b37q3wGAWZyUU41QPvNbA== check-error@^1.0.2: version "1.0.2" @@ -4942,11 +4919,6 @@ collection-visit@^1.0.0: map-visit "^1.0.0" object-visit "^1.0.0" -color-convert@^0.5.3: - version "0.5.3" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-0.5.3.tgz#bdb6c69ce660fadffe0b0007cc447e1b9f7282bd" - integrity sha1-vbbGnOZg+t/+CwAHzER+G59ygr0= - color-convert@^1.9.0: version "1.9.3" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" @@ -4966,7 +4938,7 @@ color-name@1.1.3: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= -color-name@^1.0.0, color-name@~1.1.4: +color-name@~1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== @@ -5360,6 +5332,11 @@ d@1: dependencies: es5-ext "^0.10.9" +date-fns@^2.22.1: + version "2.22.1" + resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.22.1.tgz#1e5af959831ebb1d82992bf67b765052d8f0efc4" + integrity sha512-yUFPQjrxEmIsMqlHhAhmxkuH769baF21Kk+nZwZGyrMoyLA+LugaQtC0+Tqf9CBUUULWwUJt6Q5ySI3LJDDCGg== + dateformat@^1.0.7-1.2.3: version "1.0.12" resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-1.0.12.tgz#9f124b67594c937ff706932e4a642cca8dbbfee9" @@ -9425,11 +9402,6 @@ mocha@^8.4.0: yargs-parser "20.2.4" yargs-unparser "2.0.0" -moment@^2.10.2: - version "2.24.0" - resolved "https://registry.yarnpkg.com/moment/-/moment-2.24.0.tgz#0d055d53f5052aa653c9f6eb68bb5d12bf5c2b5b" - integrity sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg== - ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" From cecb66451c7748db60ebcab29387aaf820048afa Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Sat, 19 Jun 2021 00:48:32 +0000 Subject: [PATCH 09/38] Translation update --- translations/frontend/bg.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/translations/frontend/bg.json b/translations/frontend/bg.json index a04a0754f6..af50e2574f 100644 --- a/translations/frontend/bg.json +++ b/translations/frontend/bg.json @@ -1113,7 +1113,7 @@ "buttons": { "add": "Добавете устройства чрез това устройство", "clusters": "Управление на клъстери", - "device_children": "Преглед на дъщерни", + "device_children": "Преглед на дъщерните устройства", "reconfigure": "Преконфигуриране на устройството", "remove": "Премахване на устройството", "zigbee_information": "Подпис на Zigbee устройството" From c1d571de42c41a60aac7884e3c2d6b298ccb10b0 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Sat, 19 Jun 2021 13:26:19 +0200 Subject: [PATCH 10/38] Add Select entity (#9422) Co-authored-by: Bram Kragten --- src/common/const.ts | 3 + src/data/select.ts | 25 +++ .../create-element/create-row-element.ts | 2 + .../entity-rows/hui-select-entity-row.ts | 186 ++++++++++++++++++ src/state-summary/state-card-content.js | 1 + src/state-summary/state-card-select.ts | 99 ++++++++++ 6 files changed, 316 insertions(+) create mode 100644 src/data/select.ts create mode 100644 src/panels/lovelace/entity-rows/hui-select-entity-row.ts create mode 100644 src/state-summary/state-card-select.ts diff --git a/src/common/const.ts b/src/common/const.ts index 6ec749d662..40374552e0 100644 --- a/src/common/const.ts +++ b/src/common/const.ts @@ -42,6 +42,7 @@ export const FIXED_DOMAIN_ICONS = { remote: "hass:remote", scene: "hass:palette", script: "hass:script-text", + select: "hass:format-list-bulleted", sensor: "hass:eye", simple_alarm: "hass:bell", sun: "hass:white-balance-sunny", @@ -83,6 +84,7 @@ export const DOMAINS_WITH_CARD = [ "number", "scene", "script", + "select", "timer", "vacuum", "water_heater", @@ -121,6 +123,7 @@ export const DOMAINS_HIDE_MORE_INFO = [ "input_text", "number", "scene", + "select", ]; /** Domains that should have the history hidden in the more info dialog. */ diff --git a/src/data/select.ts b/src/data/select.ts new file mode 100644 index 0000000000..9d7058d8ce --- /dev/null +++ b/src/data/select.ts @@ -0,0 +1,25 @@ +import { + HassEntityAttributeBase, + HassEntityBase, +} from "home-assistant-js-websocket"; +import { HomeAssistant } from "../types"; + +interface SelectEntityAttributes extends HassEntityAttributeBase { + options: string[]; +} + +export interface SelectEntity extends HassEntityBase { + attributes: SelectEntityAttributes; +} + +export const setSelectOption = ( + hass: HomeAssistant, + entity: string, + option: string +) => + hass.callService( + "select", + "select_option", + { option }, + { entity_id: entity } + ); diff --git a/src/panels/lovelace/create-element/create-row-element.ts b/src/panels/lovelace/create-element/create-row-element.ts index 955f4741c9..f1b63c717c 100644 --- a/src/panels/lovelace/create-element/create-row-element.ts +++ b/src/panels/lovelace/create-element/create-row-element.ts @@ -37,6 +37,7 @@ const LAZY_LOAD_TYPES = { "input-text-entity": () => import("../entity-rows/hui-input-text-entity-row"), "lock-entity": () => import("../entity-rows/hui-lock-entity-row"), "number-entity": () => import("../entity-rows/hui-number-entity-row"), + "select-entity": () => import("../entity-rows/hui-select-entity-row"), "timer-entity": () => import("../entity-rows/hui-timer-entity-row"), conditional: () => import("../special-rows/hui-conditional-row"), "weather-entity": () => import("../entity-rows/hui-weather-entity-row"), @@ -68,6 +69,7 @@ const DOMAIN_TO_ELEMENT_TYPE = { remote: "toggle", scene: "scene", script: "script", + select: "select", sensor: "sensor", timer: "timer", switch: "toggle", diff --git a/src/panels/lovelace/entity-rows/hui-select-entity-row.ts b/src/panels/lovelace/entity-rows/hui-select-entity-row.ts new file mode 100644 index 0000000000..7133db16f8 --- /dev/null +++ b/src/panels/lovelace/entity-rows/hui-select-entity-row.ts @@ -0,0 +1,186 @@ +import "@polymer/paper-item/paper-item"; +import "@polymer/paper-listbox/paper-listbox"; +import { + css, + CSSResultGroup, + html, + LitElement, + PropertyValues, + TemplateResult, +} from "lit"; +import { customElement, property, state } from "lit/decorators"; +import { classMap } from "lit/directives/class-map"; +import { ifDefined } from "lit/directives/if-defined"; +import { DOMAINS_HIDE_MORE_INFO } from "../../../common/const"; +import { stopPropagation } from "../../../common/dom/stop_propagation"; +import { computeDomain } from "../../../common/entity/compute_domain"; +import { computeStateName } from "../../../common/entity/compute_state_name"; +import "../../../components/entity/state-badge"; +import "../../../components/ha-paper-dropdown-menu"; +import { UNAVAILABLE_STATES } from "../../../data/entity"; +import { forwardHaptic } from "../../../data/haptics"; +import { SelectEntity, setSelectOption } from "../../../data/select"; +import { ActionHandlerEvent } from "../../../data/lovelace"; +import { HomeAssistant } from "../../../types"; +import { EntitiesCardEntityConfig } from "../cards/types"; +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 { LovelaceRow } from "./types"; + +@customElement("hui-select-entity-row") +class HuiSelectEntityRow extends LitElement implements LovelaceRow { + @property({ attribute: false }) public hass?: HomeAssistant; + + @state() private _config?: EntitiesCardEntityConfig; + + public setConfig(config: EntitiesCardEntityConfig): void { + if (!config || !config.entity) { + throw new Error("Entity must be specified"); + } + + this._config = config; + } + + protected shouldUpdate(changedProps: PropertyValues): boolean { + return hasConfigOrEntityChanged(this, changedProps); + } + + protected render(): TemplateResult { + if (!this.hass || !this._config) { + return html``; + } + + const stateObj = this.hass.states[this._config.entity] as + | SelectEntity + | undefined; + + if (!stateObj) { + return html` + + ${createEntityNotFoundWarning(this.hass, this._config.entity)} + + `; + } + + const pointer = + (this._config.tap_action && this._config.tap_action.action !== "none") || + (this._config.entity && + !DOMAINS_HIDE_MORE_INFO.includes(computeDomain(this._config.entity))); + + return html` + + + + ${stateObj.attributes.options + ? stateObj.attributes.options.map( + (option) => + html` + ${(stateObj.attributes.device_class && + this.hass!.localize( + `component.select.state.${stateObj.attributes.device_class}.${option}` + )) || + this.hass!.localize( + `component.select.state._.${option}` + ) || + option} + ` + ) + : ""} + + + `; + } + + protected updated(changedProps: PropertyValues) { + super.updated(changedProps); + + if (!this.hass || !this._config) { + return; + } + + const stateObj = this.hass.states[this._config.entity] as + | SelectEntity + | undefined; + + if (!stateObj) { + return; + } + + // Update selected after rendering the items or else it won't work in Firefox + if (stateObj.attributes.options) { + this.shadowRoot!.querySelector( + "paper-listbox" + )!.selected = stateObj.attributes.options.indexOf(stateObj.state); + } + } + + private _handleAction(ev: ActionHandlerEvent) { + handleAction(this, this.hass!, this._config!, ev.detail.action!); + } + + static get styles(): CSSResultGroup { + return css` + :host { + display: flex; + align-items: center; + } + ha-paper-dropdown-menu { + margin-left: 16px; + flex: 1; + } + paper-item { + cursor: pointer; + min-width: 200px; + } + .pointer { + cursor: pointer; + } + state-badge:focus { + outline: none; + background: var(--divider-color); + border-radius: 100%; + } + `; + } + + private _selectedChanged(ev): void { + const stateObj = this.hass!.states[this._config!.entity]; + const option = ev.target.selectedItem.option; + if (option === stateObj.state) { + return; + } + + forwardHaptic("light"); + + setSelectOption(this.hass!, stateObj.entity_id, option); + } +} + +declare global { + interface HTMLElementTagNameMap { + "hui-select-entity-row": HuiSelectEntityRow; + } +} diff --git a/src/state-summary/state-card-content.js b/src/state-summary/state-card-content.js index 8a365f612a..f3ee74bbf3 100644 --- a/src/state-summary/state-card-content.js +++ b/src/state-summary/state-card-content.js @@ -14,6 +14,7 @@ import "./state-card-media_player"; import "./state-card-number"; import "./state-card-scene"; import "./state-card-script"; +import "./state-card-select"; import "./state-card-timer"; import "./state-card-toggle"; import "./state-card-vacuum"; diff --git a/src/state-summary/state-card-select.ts b/src/state-summary/state-card-select.ts new file mode 100644 index 0000000000..0876f71e41 --- /dev/null +++ b/src/state-summary/state-card-select.ts @@ -0,0 +1,99 @@ +import "@polymer/paper-dropdown-menu/paper-dropdown-menu-light"; +import "@polymer/paper-item/paper-item"; +import "@polymer/paper-listbox/paper-listbox"; +import { + css, + CSSResultGroup, + html, + LitElement, + PropertyValues, + TemplateResult, +} from "lit"; +import { customElement, property } from "lit/decorators"; +import { stopPropagation } from "../common/dom/stop_propagation"; +import { computeStateName } from "../common/entity/compute_state_name"; +import "../components/entity/state-badge"; +import { SelectEntity, setSelectOption } from "../data/select"; +import type { HomeAssistant } from "../types"; + +@customElement("state-card-select") +class StateCardSelect extends LitElement { + @property({ attribute: false }) public hass!: HomeAssistant; + + @property() public stateObj!: SelectEntity; + + protected render(): TemplateResult { + return html` + + + + ${this.stateObj.attributes.options.map( + (option) => + html` + ${(this.stateObj.attributes.device_class && + this.hass.localize( + `component.select.state.${this.stateObj.attributes.device_class}.${option}` + )) || + this.hass.localize(`component.select.state._.${option}`) || + option} + ` + )} + + + `; + } + + protected updated(changedProps: PropertyValues) { + super.updated(changedProps); + if (!changedProps.has("stateObj")) { + return; + } + // Update selected after rendering the items or else it won't work in Firefox + this.shadowRoot!.querySelector( + "paper-listbox" + )!.selected = this.stateObj.attributes.options.indexOf(this.stateObj.state); + } + + private _selectedOptionChanged(ev) { + const option = ev.target.selectedItem.option; + if (option === this.stateObj.state) { + return; + } + setSelectOption(this.hass, this.stateObj.entity_id, option); + } + + static get styles(): CSSResultGroup { + return css` + :host { + display: block; + } + + state-badge { + float: left; + margin-top: 10px; + } + + paper-dropdown-menu-light { + display: block; + margin-left: 53px; + } + + paper-item { + cursor: pointer; + min-width: 200px; + } + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + "state-card-select": StateCardSelect; + } +} From 7745c10d079980ad0381211d1a809f89b96aaaa1 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Sun, 20 Jun 2021 00:48:37 +0000 Subject: [PATCH 11/38] Translation update --- translations/frontend/ca.json | 6 +++--- translations/frontend/ru.json | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/translations/frontend/ca.json b/translations/frontend/ca.json index d4a80333ba..c15932954c 100644 --- a/translations/frontend/ca.json +++ b/translations/frontend/ca.json @@ -558,9 +558,9 @@ }, "counter": { "actions": { - "decrement": "decreixement", - "increment": "increment", - "reset": "restablir" + "decrement": "decrementa", + "increment": "incrementa", + "reset": "restableix" } }, "cover": { diff --git a/translations/frontend/ru.json b/translations/frontend/ru.json index 6254ed7396..b611bf919d 100644 --- a/translations/frontend/ru.json +++ b/translations/frontend/ru.json @@ -1022,9 +1022,9 @@ }, "input_select": { "add": "Добавить", - "add_option": "Добавить опцию", - "no_options": "Добавьте доступные для выбора опции.", - "options": "Опции" + "add_option": "Добавить вариант", + "no_options": "Добавьте доступные для выбора варианты.", + "options": "Варианты" }, "input_text": { "max": "Максимальная длина", From 2c9aa1cab420dd991dd427d012c75331ce41d24e Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Mon, 21 Jun 2021 00:48:39 +0000 Subject: [PATCH 12/38] Translation update --- translations/frontend/de.json | 1 + 1 file changed, 1 insertion(+) diff --git a/translations/frontend/de.json b/translations/frontend/de.json index 1ac790a99c..89fa98cd1d 100644 --- a/translations/frontend/de.json +++ b/translations/frontend/de.json @@ -820,6 +820,7 @@ "was_unplugged": "wurde ausgesteckt", "was_unsafe": "war unsicher" }, + "retrieval_error": "Fehler beim Abrufen von Logbucheinträgen", "show_trace": "Trace anzeigen" }, "media-browser": { From 14fcff7774b615125f2629ea34c4f450e8e4758b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20S=C3=B8rensen?= Date: Mon, 21 Jun 2021 09:41:06 +0200 Subject: [PATCH 13/38] Fix secrets schema (#9446) --- hassio/src/addon-view/config/hassio-addon-config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hassio/src/addon-view/config/hassio-addon-config.ts b/hassio/src/addon-view/config/hassio-addon-config.ts index 199a5f8af2..661f3a118c 100644 --- a/hassio/src/addon-view/config/hassio-addon-config.ts +++ b/hassio/src/addon-view/config/hassio-addon-config.ts @@ -134,7 +134,7 @@ class HassioAddonConfig extends LitElement { >` : html` `} ${this._error ? html`
${this._error}
` : ""} ${!this._yamlMode || From 202d6957bc2cd98cb4c9a55d71a2ac909ff08b36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20S=C3=B8rensen?= Date: Mon, 21 Jun 2021 09:41:38 +0200 Subject: [PATCH 14/38] Allow clearing values in optional selects (#9442) Co-authored-by: Bram Kragten --- src/components/ha-form/ha-form-select.ts | 54 +++++++++++++++++++++--- 1 file changed, 49 insertions(+), 5 deletions(-) diff --git a/src/components/ha-form/ha-form-select.ts b/src/components/ha-form/ha-form-select.ts index 5cf9dc013d..c796bc8baf 100644 --- a/src/components/ha-form/ha-form-select.ts +++ b/src/components/ha-form/ha-form-select.ts @@ -1,14 +1,19 @@ +import "@material/mwc-icon-button/mwc-icon-button"; +import { mdiClose, mdiMenuDown } from "@mdi/js"; +import "@polymer/paper-input/paper-input"; import "@polymer/paper-item/paper-item"; import "@polymer/paper-listbox/paper-listbox"; +import "@polymer/paper-menu-button/paper-menu-button"; +import "@polymer/paper-ripple/paper-ripple"; import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import { customElement, property, query } from "lit/decorators"; import { fireEvent } from "../../common/dom/fire_event"; -import "../ha-paper-dropdown-menu"; +import "../ha-svg-icon"; import { HaFormElement, HaFormSelectData, HaFormSelectSchema } from "./ha-form"; @customElement("ha-form-select") export class HaFormSelect extends LitElement implements HaFormElement { - @property() public schema!: HaFormSelectSchema; + @property({ attribute: false }) public schema!: HaFormSelectSchema; @property() public data!: HaFormSelectData; @@ -26,7 +31,33 @@ export class HaFormSelect extends LitElement implements HaFormElement { protected render(): TemplateResult { return html` - + + - + `; } @@ -57,6 +88,11 @@ export class HaFormSelect extends LitElement implements HaFormElement { return Array.isArray(item) ? item[1] || item[0] : item; } + private _clearValue(ev: CustomEvent) { + ev.stopPropagation(); + fireEvent(this, "value-changed", { value: undefined }); + } + private _valueChanged(ev: CustomEvent) { if (!ev.detail.value) { return; @@ -68,8 +104,16 @@ export class HaFormSelect extends LitElement implements HaFormElement { static get styles(): CSSResultGroup { return css` - ha-paper-dropdown-menu { + paper-menu-button { display: block; + padding: 0; + } + paper-input > mwc-icon-button { + --mdc-icon-button-size: 24px; + padding: 2px; + } + .clear-button { + color: var(--secondary-text-color); } `; } From 9a4a1cb4ecc5515355e3627c2c31cce843e993e5 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Mon, 21 Jun 2021 10:38:50 +0200 Subject: [PATCH 15/38] Fix charts tooltips and legends (#9448) --- src/components/chart/ha-chart-base.ts | 146 ++++++++++++++++++++------ 1 file changed, 116 insertions(+), 30 deletions(-) diff --git a/src/components/chart/ha-chart-base.ts b/src/components/chart/ha-chart-base.ts index a976083c34..142f8252f7 100644 --- a/src/components/chart/ha-chart-base.ts +++ b/src/components/chart/ha-chart-base.ts @@ -11,6 +11,11 @@ import { classMap } from "lit/directives/class-map"; import { styleMap } from "lit/directives/style-map"; import { clamp } from "../../common/number/clamp"; +interface Tooltip extends TooltipModel { + top: string; + left: string; +} + @customElement("ha-chart-base") export default class HaChartBase extends LitElement { public chart?: Chart; @@ -24,12 +29,19 @@ export default class HaChartBase extends LitElement { @property({ attribute: false }) public options?: ChartOptions; - @state() private _tooltip?: TooltipModel; + @state() private _tooltip?: Tooltip; @state() private _height?: string; + @state() private _hiddenDatasets: Set = new Set(); + protected firstUpdated() { this._setupChart(); + this.data.datasets.forEach((dataset, index) => { + if (dataset.hidden) { + this._hiddenDatasets.add(index); + } + }); } public willUpdate(changedProps: PropertyValues): void { @@ -54,6 +66,30 @@ export default class HaChartBase extends LitElement { protected render() { return html` + ${this.options?.plugins?.legend?.display === true + ? html`
+
    + ${this.data.datasets.map( + (dataset, index) => html`
  • +
    + ${dataset.label} +
  • ` + )} +
+
` + : ""}
${this._tooltip.title}
@@ -85,15 +119,16 @@ export default class HaChartBase extends LitElement {
    ${this._tooltip.body.map( (item, i) => html`
  • - ${item.lines.join("\n")} + >
+ ${item.lines.join("\n")} ` )} @@ -134,10 +169,30 @@ export default class HaChartBase extends LitElement { enabled: false, external: (context) => this._handleTooltip(context), }, + legend: { + ...this.options?.plugins?.legend, + display: false, + }, }, }; } + private _legendClick(ev) { + if (!this.chart) { + return; + } + const index = ev.currentTarget.datasetIndex; + if (this.chart.isDatasetVisible(index)) { + this.chart.setDatasetVisibility(index, false); + this._hiddenDatasets.add(index); + } else { + this.chart.setDatasetVisibility(index, true); + this._hiddenDatasets.delete(index); + } + this.chart.update("none"); + this.requestUpdate("_hiddenDatasets"); + } + private _handleTooltip(context: { chart: Chart; tooltip: TooltipModel; @@ -146,7 +201,15 @@ export default class HaChartBase extends LitElement { this._tooltip = undefined; return; } - this._tooltip = { ...context.tooltip }; + this._tooltip = { + ...context.tooltip, + top: this.chart!.canvas.offsetTop + context.tooltip.caretY + 12 + "px", + left: + this.chart!.canvas.offsetLeft + + clamp(context.tooltip.caretX, 100, this.clientWidth - 100) - + 100 + + "px", + }; } public updateChart = (): void => { @@ -161,42 +224,76 @@ export default class HaChartBase extends LitElement { display: block; } .chartContainer { - position: relative; overflow: hidden; height: 0; transition: height 300ms cubic-bezier(0.4, 0, 0.2, 1); } + .chartLegend { + text-align: center; + } + .chartLegend li { + cursor: pointer; + display: inline-flex; + padding: 0 8px; + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; + box-sizing: border-box; + align-items: center; + color: var(--secondary-text-color); + } + .chartLegend .hidden { + text-decoration: line-through; + } + .chartLegend .bullet, + .chartTooltip .bullet { + border-width: 1px; + border-style: solid; + border-radius: 50%; + display: inline-block; + height: 16px; + margin-right: 4px; + width: 16px; + flex-shrink: 0; + box-sizing: border-box; + } + .chartTooltip .bullet { + align-self: baseline; + } + :host([rtl]) .chartTooltip .bullet { + margin-right: inherit; + margin-left: 4px; + } .chartTooltip { - padding: 4px; + padding: 8px; font-size: 90%; position: absolute; background: rgba(80, 80, 80, 0.9); color: white; border-radius: 4px; pointer-events: none; - transform: translate(-50%, 12px); z-index: 1000; width: 200px; - transition: opacity 0.15s ease-in-out; + box-sizing: border-box; } :host([rtl]) .chartTooltip { direction: rtl; } + .chartLegend ul, .chartTooltip ul { display: inline-block; padding: 0 0px; - margin: 5px 0 0 0; + margin: 8px 0 0 0; width: 100%; } .chartTooltip ul { - margin: 0 3px; + margin: 0 4px; } .chartTooltip li { - display: block; + display: flex; white-space: pre-line; - } - .chartTooltip li::first-line { - line-height: 0; + align-items: center; + line-height: 16px; } .chartTooltip .title { text-align: center; @@ -207,17 +304,6 @@ export default class HaChartBase extends LitElement { font-weight: 300; word-break: break-all; } - .chartTooltip em { - border-radius: 4px; - display: inline-block; - height: 10px; - margin-right: 4px; - width: 10px; - } - :host([rtl]) .chartTooltip em { - margin-right: inherit; - margin-left: 4px; - } `; } } From 4fbc31d0b06d4d7c794381d09b17205748a2a01b Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Tue, 22 Jun 2021 00:48:29 +0000 Subject: [PATCH 16/38] Translation update --- translations/frontend/fr.json | 14 ++- translations/frontend/hu.json | 162 +++++++++++++++++++--------------- 2 files changed, 102 insertions(+), 74 deletions(-) diff --git a/translations/frontend/fr.json b/translations/frontend/fr.json index 2088f1fe42..8955807461 100644 --- a/translations/frontend/fr.json +++ b/translations/frontend/fr.json @@ -380,6 +380,7 @@ "snapshot": { "addons": "Modules complémentaires", "available_snapshots": "Instantanés disponibles", + "confirm_password": "Confirmez le mot de passe de l'instantané", "could_not_create": "Impossible de créer un instantané", "create": "Créer", "create_blocked_not_running": "La création d’un instantané n’est pas possible en ce moment car le système est en état {state}.", @@ -402,6 +403,7 @@ "password": "Mot de passe", "password_protected": "protégé par mot de passe", "password_protection": "Protection par mot de passe", + "passwords_not_matching": "Les mots de passe ne correspondent pas", "security": "Sécurité", "type": "Type", "upload_snapshot": "Téléverser un instantané" @@ -798,6 +800,7 @@ "was_unplugged": "était débranché", "was_unsafe": "n'était pas sûr" }, + "retrieval_error": "Erreur lors de la récupération d'une entrée du journal", "show_trace": "Afficher la trace" }, "media-browser": { @@ -3857,10 +3860,19 @@ "intro": "Êtes-vous prêt à réveiller votre maison, à récupérer votre vie privée et à rejoindre une communauté mondiale de bricoleurs?", "next": "Suivant", "restore": { + "addons": "Modules complémentaires", + "confirm_password": "Confirmez le mot de passe de l'instantané", "description": "Vous pouvez également restaurer à partir d'un instantané précédent.", + "folders": "Dossiers", + "full_snapshot": "Instantané complet", "hide_log": "Masquer le journal", "in_progress": "Restauration en cours", - "show_log": "Afficher le journal" + "partial_snapshot": "Instantané partiel", + "password": "Mot de passe de l'instantané", + "password_protection": "Protection par mot de passe", + "select_type": "Sélectionnez les éléments à restaurer", + "show_log": "Afficher le journal", + "type": "Type d'instantané" }, "user": { "create_account": "Créer un compte", diff --git a/translations/frontend/hu.json b/translations/frontend/hu.json index 7f79d1ea49..b4e93321d6 100644 --- a/translations/frontend/hu.json +++ b/translations/frontend/hu.json @@ -272,7 +272,7 @@ "addon_new_version": "Új verzió érhető el", "addon_running": "A bővítmény fut", "addon_stopped": "A bővítmény leállt", - "addons": "Bővítmények", + "addons": "Telepített bővítmények", "no_addons": "Még nincs telepítve egyetlen bővítmény sem. A kezdéshez menjen át a bővítmény boltba!" }, "dialog": { @@ -330,6 +330,7 @@ "create": "Létrehozás", "create_blocked_not_running": "Jelenleg nem lehet pillanatképet létrehozni, mivel a rendszer {state} állapotban van.", "create_snapshot": "Pillanatkép létrehozása", + "delete_snapshot_confirm": "törlés", "description": "A pillanatképekkel könnyen hozhatsz létre vagy tölthetsz vissza mentést a teljes Home Assistant példányodról.", "enter_password": "Add meg a jelszót", "folder": { @@ -341,14 +342,14 @@ }, "folders": "Mappák", "full_snapshot": "Teljes pillanatkép", - "name": "Név", + "name": "Pillanatkép neve", "no_snapshots": "Nincs még egyetlen pillanatképed sem létrehozva.", "partial_snapshot": "Részleges pillanatkép", - "password": "Jelszó", + "password": "Pillanatkép jelszava", "password_protected": "jelszóval védett", "password_protection": "Jelszóvédelem", "security": "Biztonság", - "type": "Típus", + "type": "Pillanatkép típusa", "upload_snapshot": "Pillanatkép feltöltése" }, "store": { @@ -444,7 +445,7 @@ }, "automation": { "last_triggered": "Utoljára aktiválva", - "trigger": "Végrehajt" + "trigger": "Műveletek futtatása" }, "camera": { "not_available": "Kép nem áll rendelkezésre" @@ -497,7 +498,7 @@ "brightness": "Fényerő", "color_temperature": "Színhőmérséklet", "effect": "Hatás", - "white_value": "Fehér érték" + "white_value": "Fehér fényerő" }, "lock": { "code": "Kód", @@ -643,6 +644,9 @@ "no_match": "Nem található egyező terület", "show_areas": "Területek megjelenítése" }, + "attributes": { + "expansion_header": "Attribútumok" + }, "blueprint-picker": { "add_user": "Felhasználó hozzáadása", "remove_user": "Felhasználó eltávolítása", @@ -951,8 +955,8 @@ "remove_intro": "Ha az entitás már nincs használatban, akkor nyugodtan eltávolíthatod." }, "script": { - "last_action": "Utolsó Művelet", - "last_triggered": "Utoljára aktivált" + "last_action": "Utolsó művelet", + "last_triggered": "Utolsó aktiválás" }, "settings": "Entitás beállítások", "sun": { @@ -1020,40 +1024,40 @@ "zone": "Zónák" }, "reload": { - "automation": "Automatizálások újratöltése", - "command_line": "Parancssori entitások újratöltése", - "core": "Lokáció és testreszabások újratöltése", - "filesize": "Fájlméret entitások újratöltése", - "filter": "Szűrőentitások újratöltése", - "generic": "Általános IP kamera entitások újratöltése", - "generic_thermostat": "Általános termosztát entitások újratöltése", - "group": "Csoportok, csoport entitások és értesítési szolgáltatások újratöltése", - "history_stats": "Előzmény statisztika entitások újratöltése", - "homekit": "HomeKit újratöltése", - "input_boolean": "Logikai változó bemenetek újratöltése", - "input_datetime": "Időpont bemenetek újratöltése", - "input_number": "Szám bemenetek újratöltése", - "input_select": "Választási bemenetek újratöltése", - "input_text": "Szöveg bemenetek újratöltése", - "min_max": "Min/max entitások újratöltése", - "mqtt": "Manuálisan konfigurált MQTT entitások újratöltése", - "person": "Személyek újratöltése", - "ping": "Ping bináris érzékelő entitások újratöltése", - "reload": "{domain} újratöltése", - "rest": "Rest entitások és értesítési szolgáltatások újratöltése", - "rpi_gpio": "Raspberry Pi GPIO entitások újratöltése", - "scene": "Jelenetek újratöltése", - "script": "Szkriptek újratöltése", - "smtp": "SMTP értesítési szolgáltatások újratöltése", - "statistics": "Statisztikai entitások újratöltése", - "telegram": "Telegram értesítési szolgáltatások újratöltése", - "template": "Sablon entitások újratöltése", - "trend": "Trend entitások újratöltése", - "universal": "Univerzális médialejátszó entitások újratöltése", - "zone": "Zónák újratöltése" + "automation": "Automatizálások", + "command_line": "Parancssori entitások", + "core": "Lokáció és testreszabások", + "filesize": "Fájlméret entitások", + "filter": "Szűrőentitások", + "generic": "Általános IP kamera entitások", + "generic_thermostat": "Általános termosztát entitások", + "group": "Csoportok, csoport entitások és értesítési szolgáltatások", + "history_stats": "Előzmény statisztika entitások", + "homekit": "HomeKit", + "input_boolean": "Logikai változó bemenetek", + "input_datetime": "Időpont bemenetek", + "input_number": "Szám bemenetek", + "input_select": "Választási bemenetek", + "input_text": "Szöveg bemenetek", + "min_max": "Min/max entitások", + "mqtt": "Manuálisan konfigurált MQTT entitások", + "person": "Személyek", + "ping": "Ping bináris érzékelő entitások", + "reload": "{domain}", + "rest": "Rest entitások és értesítési szolgáltatások", + "rpi_gpio": "Raspberry Pi GPIO entitások", + "scene": "Jelenetek", + "script": "Szkriptek", + "smtp": "SMTP értesítési szolgáltatások", + "statistics": "Statisztikai entitások", + "telegram": "Telegram értesítési szolgáltatások", + "template": "Sablon entitások", + "trend": "Trend entitások", + "universal": "Univerzális médialejátszó entitások", + "zone": "Zónák" }, "server_control": { - "perform_action": "{action} Szerver", + "perform_action": "{action} szerver", "restart": "Újraindítás", "stop": "Leállítás" } @@ -1576,11 +1580,12 @@ "info_state_reporting": "Ha engedélyezed az állapotjelentést, akkor a Home Assistant minden állapotváltozást el fog küldeni a feltárt entitásokról az Amazon-nak. Ez lehetővé teszi, hogy mindig láthasd a legfrissebb állapotokat az Alexa alkalmazásban, és az állapotváltozásokkal rutinokat hozhass létre.", "manage_entities": "Entitások kezelése", "state_reporting_error": "Nem lehet {enable_disable} az állapotjelentést.", - "sync_entities": "Entitások szinkronizálása", + "sync_entities": "Entitások szinkronizálása az Amazonnal", "sync_entities_error": "Nem sikerült szinkronizálni az entitásokat:", "title": "Alexa" }, "connected": "Csatlakoztatva", + "connecting": "Csatlakozás...", "connection_status": "Felhő kapcsolat állapota", "fetching_subscription": "Előfizetés lekérése...", "google": { @@ -1658,7 +1663,7 @@ "description_login": "Bejelentkezve mint {email}", "description_not_login": "Nincs bejelentkezve", "dialog_certificate": { - "certificate_expiration_date": "Tanúsítvány lejárati dátuma", + "certificate_expiration_date": "Tanúsítvány lejárati dátuma:", "certificate_information": "Tanúsítvány-információ", "close": "Bezárás", "fingerprint": "Tanúsítvány ujjlenyomata:", @@ -2444,39 +2449,39 @@ "description": "A Home Assistant szerver újraindítása és leállítása", "section": { "reloading": { - "automation": "Automatizálások újratöltése", - "command_line": "Parancssori entitások újratöltése", - "core": "Lokáció és testreszabások újratöltése", - "filesize": "Fájlméret entitások újratöltése", - "filter": "Szűrőentitások újratöltése", - "generic": "Általános IP kamera entitások újratöltése", - "generic_thermostat": "Általános termosztát entitások újratöltése", - "group": "Csoportok, csoport entitások és értesítési szolgáltatások újratöltése", + "automation": "Automatizálások", + "command_line": "Parancssori entitások", + "core": "Lokáció és testreszabások", + "filesize": "Fájlméret entitások", + "filter": "Szűrőentitások", + "generic": "Általános IP kamera entitások", + "generic_thermostat": "Általános termosztát entitások", + "group": "Csoportok, csoport entitások és értesítési szolgáltatások", "heading": "YAML konfiguráció újratöltése", - "history_stats": "Előzmény statisztika entitások újratöltése", - "homekit": "HomeKit újratöltése", - "input_boolean": "Logikai változó bemenetek újratöltése", - "input_datetime": "Időpont bemenetek újratöltése", - "input_number": "Szám bemenetek újratöltése", - "input_select": "Választási bemenetek újratöltése", - "input_text": "Szöveg bemenetek újratöltése", + "history_stats": "Előzmény statisztika entitások", + "homekit": "HomeKit", + "input_boolean": "Logikai változó bemenetek", + "input_datetime": "Időpont bemenetek", + "input_number": "Szám bemenetek", + "input_select": "Választási bemenetek", + "input_text": "Szöveg bemenetek", "introduction": "A Home Assistant bizonyos részei újraindítás nélkül újratölthetőek. Újratöltéskor az aktuálisan betöltött YAML konfiguráció helyére betöltődik az új.", - "min_max": "Min/max entitások újratöltése", - "mqtt": "Manuálisan konfigurált MQTT entitások újratöltése", - "person": "Személyek újratöltése", - "ping": "Ping bináris érzékelő entitások újratöltése", - "reload": "{domain} újratöltése", - "rest": "Rest entitások és értesítési szolgáltatások újratöltése", - "rpi_gpio": "Raspberry Pi GPIO entitások újratöltése", - "scene": "Jelenetek újratöltése", - "script": "Szkriptek újratöltése", - "smtp": "SMTP értesítési szolgáltatások újratöltése", - "statistics": "Statisztikai entitások újratöltése", - "telegram": "Telegram értesítési szolgáltatások újratöltése", - "template": "Sablon entitások újratöltése", - "trend": "Trend entitások újratöltése", - "universal": "Univerzális médialejátszó entitások újratöltése", - "zone": "Zónák újratöltése" + "min_max": "Min/max entitások", + "mqtt": "Manuálisan konfigurált MQTT entitások", + "person": "Személyek", + "ping": "Ping bináris érzékelő entitások", + "reload": "{domain}", + "rest": "Rest entitások és értesítési szolgáltatások", + "rpi_gpio": "Raspberry Pi GPIO entitások", + "scene": "Jelenetek", + "script": "Szkriptek", + "smtp": "SMTP értesítési szolgáltatások", + "statistics": "Statisztikai entitások", + "telegram": "Telegram értesítési szolgáltatások", + "template": "Sablon entitások", + "trend": "Trend entitások", + "universal": "Univerzális médialejátszó entitások", + "zone": "Zónák" }, "server_management": { "confirm_restart": "Biztosan újra szeretnéd indítani a Home Assistant-ot?", @@ -3699,6 +3704,17 @@ "primary_color": "Elsődleges szín", "reset": "Visszaállítás" }, + "time_format": { + "description": "Válaszd ki az időformátumot.", + "dropdown_label": "Időformátum", + "formats": { + "12": "12 óra (AM/PM)", + "24": "24 óra", + "language": "Automatikus (nyelvi beállítás használata)", + "system": "Rendszer területi beállításainak használata" + }, + "header": "Időformátum" + }, "vibrate": { "description": "Rezgés engedélyezése vagy tiltása ezen az eszközön az eszközök vezérlésekor.", "header": "Rezgés" From c327fe11b82dc5b49cde9588c92bc312db365f00 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Wed, 23 Jun 2021 00:48:18 +0000 Subject: [PATCH 17/38] Translation update --- translations/frontend/ja.json | 83 +++++++++++++++++++++++++++++++++-- 1 file changed, 79 insertions(+), 4 deletions(-) diff --git a/translations/frontend/ja.json b/translations/frontend/ja.json index c096f0fec0..fd0a649eb9 100644 --- a/translations/frontend/ja.json +++ b/translations/frontend/ja.json @@ -312,6 +312,9 @@ "no_addons": "まだアドオンがインストールされていません。アドオンストアにアクセスして始めましょう!" }, "dialog": { + "hardware": { + "search": "ハードウェアの検索" + }, "network": { "connected_to": "{ssid}に接続しました", "dhcp": "DHCP", @@ -360,7 +363,9 @@ }, "my": { "error": "不明なエラーが発生しました", + "error_addon_no_ingress": "要求されたアドオンは入力をサポートしていません", "error_addon_not_found": "アドオンが見つかりません", + "error_addon_not_started": "要求されたアドオンは実行されていません。最初に起動してください", "faq_link": "マイホームアシスタントFAQ", "not_supported": "このリダイレクトは、HomeAssistantインスタンスではサポートされていません。 サポートされているリダイレクトとそれらが導入されたバージョンについては、{link}を確認してください。" }, @@ -373,13 +378,19 @@ "snapshot": { "addons": "アドオン", "available_snapshots": "利用可能なスナップショット", + "confirm_password": "スナップショットパスワードの確認", "could_not_create": "スナップショットを作成できませんでした。", "create": "作成", "create_blocked_not_running": "システムが {state} 状態であるため、スナップショットの作成は現在できません。", "create_snapshot": "スナップショットの作成", "created": "作成", + "delete_selected": "選択したスナップショットの削除", + "delete_snapshot_confirm": "削除", + "delete_snapshot_text": "{number}を削除しますか?{number, plural,\n one {snapshot}\n other {snapshots}\n}?", + "delete_snapshot_title": "スナップショットの削除", "description": "スナップショットでは、Home Assistantインスタンスのすべてのデータを簡単にバックアップして復元することができます。", "enter_password": "パスワードを入力してください。", + "failed_to_delete": "削除できませんでした", "folder": { "addons/local": "ローカルアドオン", "homeassistant": "HomeAssistantの設定", @@ -395,7 +406,9 @@ "password": "パスワード", "password_protected": "パスワードで保護されています", "password_protection": "パスワード保護", + "passwords_not_matching": "パスワードが一致しません", "security": "セキュリティ", + "selected": "{number}が選択されました", "type": "タイプ", "upload_snapshot": "スナップショットのアップロード" }, @@ -555,6 +568,7 @@ }, "light": { "brightness": "明るさ", + "color_brightness": "色の明るさ", "color_temperature": "色温度", "effect": "効果", "white_value": "白さの値" @@ -708,6 +722,9 @@ "no_match": "一致するエリアが見つかりません", "show_areas": "エリアを表示" }, + "attributes": { + "expansion_header": "属性" + }, "blueprint-picker": { "add_user": "ユーザーの追加", "remove_user": "ユーザーの削除", @@ -786,6 +803,7 @@ "was_unplugged": "プラグが抜かれました", "was_unsafe": "安全ではなかった" }, + "retrieval_error": "ログ ブックエントリの取得中にエラーが発生しました", "show_trace": "トレースの表示" }, "media-browser": { @@ -1178,7 +1196,15 @@ } }, "zha_reconfigure_device": { - "heading": "デバイスを再設定" + "attribute": "属性", + "button_hide": "詳細を非表示", + "button_show": "詳細を表示", + "configuration_complete": "デバイスの再構成が完了しました。", + "configuration_failed": "デバイスの再構成に失敗しました。追加情報はログで入手できる場合があります。", + "configuring_alt": "設定", + "heading": "デバイスを再設定", + "in_progress": "デバイスを再構成中です。これには時間がかかる場合があります。", + "start_reconfiguration": "再構成を開始" } }, "duration": { @@ -1241,7 +1267,8 @@ "caption": "エリア", "data_table": { "area": "エリア", - "devices": "デバイス" + "devices": "デバイス", + "entities": "エンティティ" }, "delete": { "confirmation_text": "このエリアのすべてのデバイスは割り当て解除されます。", @@ -1253,6 +1280,7 @@ "create": "作成", "default_name": "新しいエリア", "delete": "削除", + "linked_entities_caption": "エンティティ", "name": "名前", "name_required": "名前は必須です", "unknown_error": "不明なエラー", @@ -1679,6 +1707,7 @@ "title": "Alexa" }, "connected": "接続", + "connecting": "接続しています...", "connection_status": "クラウド接続の状態", "fetching_subscription": "サブスクリプションを取得しています。", "google": { @@ -1713,6 +1742,11 @@ "instance_is_available": "インスタンスは次の場所にあります", "instance_will_be_available": "インスタンスは次の時点で利用可能になります。", "link_learn_how_it_works": "仕組みを学ぶ", + "not_connected": "接続されていません", + "remote_enabled": { + "caption": "自動的に接続", + "description": "このオプションを有効にすると、ホームアシスタントのインスタンスが常にリモートでアクセスできるようになります。" + }, "title": "リモートコントロール" }, "sign_out": "サインアウト", @@ -2016,7 +2050,8 @@ "scripts": "スクリプト", "unknown_error": "不明なエラー", "unnamed_device": "名前のないデバイス", - "update": "更新" + "update": "更新", + "update_device_error": "デバイスの更新に失敗しました" }, "entities": { "caption": "エンティティ", @@ -2123,6 +2158,7 @@ "license": "Apache 2.0ライセンスの下で公開", "path_configuration": "ディスク上に configuration.yaml へのパス: {path}", "server": "サーバー", + "setup_time": "設定時間", "source": "ソース:", "system_health_error": "「システムの正常性」コンポーネントが有効されていません、configuration.yaml に 'system_health:' を追加してください。", "system_health": { @@ -2173,6 +2209,11 @@ "rename": "名前を変更", "restart_confirm": "ホーム アシスタントを再起動して、この統合の削除を完了します。", "services": "{count} {count, plural,\n one {サービス}\n other {サービス}\n}", + "state": { + "loaded": "読み込み", + "not_loaded": "読み込まれていません", + "setup_retry": "設定をやり直す" + }, "system_options": "システムオプション", "unnamed_entry": "名前のないエントリ" }, @@ -2190,6 +2231,7 @@ }, "finish": "完了", "loading_first_time": "インテグレーションのインストールを完了するまでお待ちください", + "next": "次", "not_all_required_fields": "必須フィールドの一覧に入力するわけではありません。", "not_loaded": "インテグレーションファイルを読み込めませんでした。ホームアシスタントを再起動してください。", "pick_flow_step": { @@ -2528,6 +2570,7 @@ "add_scene": "シーンを追加", "delete_confirm": "このシーンを削除してもよろしいですか?", "delete_scene": "シーンを削除", + "duplicate_scene": "シーンの複製", "edit_scene": "シーンを編集", "header": "シーンエディター", "headers": { @@ -2853,6 +2896,8 @@ "follow_device_instructions": "デバイスに付属の指示に従って、デバイスでペアリングを開始します。", "inclusion_failed": "ノードを追加できませんでした。詳細については、ログを確認してください。", "inclusion_finished": "ノードが追加されました。ノードのバックグラウンドでの設定が完了すると、すべてのエンティティが表示されるまで数分かかる場合があります。", + "interview_failed": "デバイスのインタビューに失敗しました。追加情報がログに残っている場合があります。", + "interview_started": "デバイスはインタビュー中です。これには時間がかかる場合があります。", "introduction": "このウィザードでは、Z-Wave ネットワークにノードを追加する手順を説明します。", "secure_inclusion_warning": "セキュアなデバイスには、追加の帯域幅が必要です。安全なデバイスが多すぎると、Z-Waveネットワークが遅くなる可能性があります。ロックやガレージドアオープナーなど、必要なデバイスにのみ安全な組み込みを使用することをお勧めします。", "start_inclusion": "インクルージョンを開始", @@ -2890,7 +2935,11 @@ "node_status": "ノードの状態", "zwave_info": "Z-Wave情報" }, + "logs": { + "log_level_changed": "ログレベルが変更されました。{level}に変更されました。" + }, "navigation": { + "logs": "ログ", "network": "ネットワーク" }, "network_status": { @@ -2915,6 +2964,12 @@ "unknown": "不明" }, "reinterview_node": { + "battery_device_warning": "再会する前に、バッテリー駆動のデバイスをスリープ解除する必要があります。デバイスをウェイクアップする方法については、デバイスのマニュアルを参照してください。", + "in_progress": "デバイスがインタビュー中です。これには時間がかかる場合があります。", + "interview_complete": "デバイスのインタビューが完了しました。", + "interview_failed": "デバイスのインタビューに失敗しました。追加情報がログに残っている場合があります。", + "run_in_background": "このダイアログを閉じても、インタビューはバックグラウンドで継続されます。", + "start_reinterview": "再インタビューを開始", "title": "Z-Waveデバイスへの再インタビュー" }, "remove_node": { @@ -3777,10 +3832,19 @@ "intro": "あなたはあなたの家を目覚めさせ、あなたのプライバシーを取り戻し、ティンカーの世界的なコミュニティに参加する準備ができていますか?", "next": "次", "restore": { + "addons": "アドオン", + "confirm_password": "スナップショットパスワードの確認", "description": "以前のスナップショットから復元することもできます。", + "folders": "フォルダ", + "full_snapshot": "フルスナップショット", "hide_log": "ログ全体を非表示", "in_progress": "復元中", - "show_log": "ログ全体を表示" + "partial_snapshot": "部分的スナップショット", + "password": "スナップショットパスワード", + "password_protection": "パスワード保護", + "select_type": "何を復元するかを選択します", + "show_log": "ログ全体を表示", + "type": "スナップショットタイプ" }, "user": { "create_account": "アカウントの作成", @@ -3924,6 +3988,17 @@ "primary_color": "プライマリーの色", "reset": "リセット" }, + "time_format": { + "description": "時刻のフォーマットを選択します。", + "dropdown_label": "時間形式", + "formats": { + "12": "12時間(AM / PM)", + "24": "24時間", + "language": "自動 (言語設定を使用)", + "system": "システムロケールを使用する" + }, + "header": "時間形式" + }, "vibrate": { "description": "デバイスを制御するときに、このデバイスのバイブレーションを有効または無効にします。", "header": "バイブレーション機能" From d93db169631f6862164a80c40e326d3bc7454211 Mon Sep 17 00:00:00 2001 From: Martin Hjelmare Date: Thu, 24 Jun 2021 13:21:30 +0200 Subject: [PATCH 18/38] Add button for zwave_js options flow (#9001) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Joakim Sørensen --- .../zwave_js/zwave_js-config-dashboard.ts | 18 ++++++++++++++++++ src/translations/en.json | 3 ++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/panels/config/integrations/integration-panels/zwave_js/zwave_js-config-dashboard.ts b/src/panels/config/integrations/integration-panels/zwave_js/zwave_js-config-dashboard.ts index 44c66b7ba4..07e0b72d9f 100644 --- a/src/panels/config/integrations/integration-panels/zwave_js/zwave_js-config-dashboard.ts +++ b/src/panels/config/integrations/integration-panels/zwave_js/zwave_js-config-dashboard.ts @@ -29,6 +29,8 @@ import "../../../ha-config-section"; import { showZWaveJSAddNodeDialog } from "./show-dialog-zwave_js-add-node"; import { showZWaveJSRemoveNodeDialog } from "./show-dialog-zwave_js-remove-node"; import { configTabs } from "./zwave_js-config-router"; +import { getConfigEntries } from "../../../../../data/config_entries"; +import { showOptionsFlowDialog } from "../../../../../dialogs/config-flow/show-dialog-options-flow"; @customElement("zwave_js-config-dashboard") class ZWaveJSConfigDashboard extends LitElement { @@ -162,6 +164,11 @@ class ZWaveJSConfigDashboard extends LitElement { "ui.panel.config.zwave_js.common.remove_node" )} + + ${this.hass.localize( + "ui.panel.config.zwave_js.common.reconfigure_server" + )} + @@ -262,6 +269,17 @@ class ZWaveJSConfigDashboard extends LitElement { ); } + private async _openOptionFlow() { + if (!this.configEntryId) { + return; + } + const configEntries = await getConfigEntries(this.hass); + const configEntry = configEntries.find( + (entry) => entry.entry_id === this.configEntryId + ); + showOptionsFlowDialog(this, configEntry!); + } + private async _dumpDebugClicked() { await this._fetchNodeStatus(); diff --git a/src/translations/en.json b/src/translations/en.json index 030742b168..4896d0c5bd 100755 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -2587,7 +2587,8 @@ "home_id": "Home ID", "close": "Close", "add_node": "Add Node", - "remove_node": "Remove Node" + "remove_node": "Remove Node", + "reconfigure_server": "Re-configure Server" }, "dashboard": { "header": "Manage your Z-Wave Network", From a4aba93d5703f55ade19d5046b9897d2672ee26e Mon Sep 17 00:00:00 2001 From: rianadon Date: Thu, 24 Jun 2021 14:14:36 -0700 Subject: [PATCH 19/38] Add input elements to login page for password managers (#9369) --- src/auth/ha-auth-flow.ts | 27 +++++- src/auth/ha-password-manager-polyfill.ts | 110 +++++++++++++++++++++++ 2 files changed, 134 insertions(+), 3 deletions(-) create mode 100644 src/auth/ha-password-manager-polyfill.ts diff --git a/src/auth/ha-auth-flow.ts b/src/auth/ha-auth-flow.ts index 85dcf053e9..c04f91acc9 100644 --- a/src/auth/ha-auth-flow.ts +++ b/src/auth/ha-auth-flow.ts @@ -7,6 +7,7 @@ import { PropertyValues, TemplateResult, } from "lit"; +import "./ha-password-manager-polyfill"; import { property, state } from "lit/decorators"; import "../components/ha-form/ha-form"; import "../components/ha-markdown"; @@ -20,7 +21,7 @@ import { litLocalizeLiteMixin } from "../mixins/lit-localize-lite-mixin"; type State = "loading" | "error" | "step"; class HaAuthFlow extends litLocalizeLiteMixin(LitElement) { - @property() public authProvider?: AuthProvider; + @property({ attribute: false }) public authProvider?: AuthProvider; @property() public clientId?: string; @@ -37,7 +38,15 @@ class HaAuthFlow extends litLocalizeLiteMixin(LitElement) { @state() private _errorMessage?: string; protected render() { - return html`
${this._renderForm()}
`; + return html` +
${this._renderForm()}
+ + `; } protected firstUpdated(changedProps: PropertyValues) { @@ -231,11 +240,17 @@ class HaAuthFlow extends litLocalizeLiteMixin(LitElement) { await this.updateComplete; // 100ms to give all the form elements time to initialize. setTimeout(() => { - const form = this.shadowRoot!.querySelector("ha-form"); + const form = this.renderRoot.querySelector("ha-form"); if (form) { (form as any).focus(); } }, 100); + + setTimeout(() => { + this.renderRoot.querySelector( + "ha-password-manager-polyfill" + )!.boundingRect = this.getBoundingClientRect(); + }, 500); } private _stepDataChanged(ev: CustomEvent) { @@ -329,3 +344,9 @@ class HaAuthFlow extends litLocalizeLiteMixin(LitElement) { } } customElements.define("ha-auth-flow", HaAuthFlow); + +declare global { + interface HTMLElementTagNameMap { + "ha-auth-flow": HaAuthFlow; + } +} diff --git a/src/auth/ha-password-manager-polyfill.ts b/src/auth/ha-password-manager-polyfill.ts new file mode 100644 index 0000000000..1a2e765e05 --- /dev/null +++ b/src/auth/ha-password-manager-polyfill.ts @@ -0,0 +1,110 @@ +import { html, LitElement, TemplateResult } from "lit"; +import { customElement, property } from "lit/decorators"; +import { fireEvent } from "../common/dom/fire_event"; +import { HaFormSchema } from "../components/ha-form/ha-form"; +import { DataEntryFlowStep } from "../data/data_entry_flow"; + +declare global { + interface HTMLElementTagNameMap { + "ha-password-manager-polyfill": HaPasswordManagerPolyfill; + } + interface HASSDomEvents { + "form-submitted": undefined; + } +} + +const ENABLED_HANDLERS = [ + "homeassistant", + "legacy_api_password", + "command_line", +]; + +@customElement("ha-password-manager-polyfill") +export class HaPasswordManagerPolyfill extends LitElement { + @property({ attribute: false }) public step?: DataEntryFlowStep; + + @property({ attribute: false }) public stepData: any; + + @property({ attribute: false }) public boundingRect?: DOMRect; + + protected createRenderRoot() { + // Add under document body so the element isn't placed inside any shadow roots + return document.body; + } + + private get styles() { + return ` + .password-manager-polyfill { + position: absolute; + top: ${this.boundingRect?.y || 148}px; + left: calc(50% - ${(this.boundingRect?.width || 360) / 2}px); + width: ${this.boundingRect?.width || 360}px; + opacity: 0; + z-index: -1; + } + .password-manager-polyfill input { + width: 100%; + height: 62px; + padding: 0; + border: 0; + } + .password-manager-polyfill input[type="submit"] { + width: 0; + height: 0; + } + `; + } + + protected render(): TemplateResult { + if ( + this.step && + this.step.type === "form" && + this.step.step_id === "init" && + ENABLED_HANDLERS.includes(this.step.handler[0]) + ) { + return html` + + `; + } + return html``; + } + + private render_input(schema: HaFormSchema): TemplateResult | string { + const inputType = schema.name.includes("password") ? "password" : "text"; + if (schema.type !== "string") { + return ""; + } + return html` + + `; + } + + private _handleSubmit(ev: Event) { + ev.preventDefault(); + fireEvent(this, "form-submitted"); + } + + private _valueChanged(ev: Event) { + const target = ev.target! as HTMLInputElement; + this.stepData = { ...this.stepData, [target.id]: target.value }; + fireEvent(this, "value-changed", { + value: this.stepData, + }); + } +} From 27730e65e7e1984cced8526ef6f28ee27b40ee90 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Fri, 25 Jun 2021 00:47:31 +0000 Subject: [PATCH 20/38] Translation update --- translations/frontend/en.json | 1 + translations/frontend/es.json | 1 + translations/frontend/et.json | 1 + translations/frontend/ja.json | 4 +- translations/frontend/lt.json | 1273 +++++++++++++++++++++++++++- translations/frontend/ru.json | 1 + translations/frontend/zh-Hans.json | 1 + 7 files changed, 1234 insertions(+), 48 deletions(-) diff --git a/translations/frontend/en.json b/translations/frontend/en.json index 17b1da1b4d..c105428f82 100644 --- a/translations/frontend/en.json +++ b/translations/frontend/en.json @@ -2965,6 +2965,7 @@ "home_id": "Home ID", "network": "Network", "node_id": "Node ID", + "reconfigure_server": "Re-configure Server", "remove_node": "Remove Node" }, "dashboard": { diff --git a/translations/frontend/es.json b/translations/frontend/es.json index f898948e66..4ad16212b8 100644 --- a/translations/frontend/es.json +++ b/translations/frontend/es.json @@ -2965,6 +2965,7 @@ "home_id": "ID de casa", "network": "Red", "node_id": "ID del Nodo", + "reconfigure_server": "Reconfigurar el servidor", "remove_node": "Eliminar Nodo" }, "dashboard": { diff --git a/translations/frontend/et.json b/translations/frontend/et.json index 912eddb469..2f47fa463a 100644 --- a/translations/frontend/et.json +++ b/translations/frontend/et.json @@ -2965,6 +2965,7 @@ "home_id": "Kodu ID", "network": "Võrk", "node_id": "Sõlme ID", + "reconfigure_server": "Taasseadista server", "remove_node": "Eemalda sõlm" }, "dashboard": { diff --git a/translations/frontend/ja.json b/translations/frontend/ja.json index fd0a649eb9..839f80267b 100644 --- a/translations/frontend/ja.json +++ b/translations/frontend/ja.json @@ -1250,12 +1250,12 @@ "notification_toast": { "connection_lost": "接続が切れました。再接続中...", "dismiss": "閉じる", - "integration_starting": "{integration}を開始すると、完了するまですべてが利用できるわけではありません。", + "integration_starting": "{integration}の開始、完了するまですべてが利用できるわけではありません。", "service_call_failed": "サービス{service}の呼び出しに失敗しました。", "started": "ホームアシスタントが開始されました!", "starting": "Home Assistantが起動中です。全て利用可能なるまでもうしばらくお待ち下さい。", "triggered": "トリガーしました {name}", - "wrapping_up_startup": "スタートアップの締めくくりとして、終了するまですべてが利用できるわけではありません。" + "wrapping_up_startup": "起動開始、起動完了するまですべてが利用できるわけではありません。" }, "panel": { "config": { diff --git a/translations/frontend/lt.json b/translations/frontend/lt.json index 5016bb31cf..1491572285 100644 --- a/translations/frontend/lt.json +++ b/translations/frontend/lt.json @@ -1,7 +1,8 @@ { "config_entry": { "disabled_by": { - "device": "Įrenginys" + "device": "Įrenginys", + "user": "Vartotojas" } }, "groups": { @@ -11,6 +12,7 @@ "system-users": "Vartotojai" }, "panel": { + "calendar": "Kalendorius", "config": "Konfigūracija", "developer_tools": "Kūrėjo įrankiai", "history": "Istorija", @@ -28,8 +30,29 @@ "off": "Išjungta", "on": "Įjungta" }, + "hvac_action": { + "cooling": "Aušinimas", + "drying": "Džiovinimas", + "fan": "Ventiliatorius", + "heating": "Šildymas", + "idle": "Laukimo režimas", + "off": "Išjungta" + }, "preset_mode": { - "away": "Išvykęs" + "activity": "Veikla", + "away": "Išvykęs", + "boost": "Padidinti", + "comfort": "Komfortas", + "eco": "Eko", + "home": "Namai", + "sleep": "Miegoti" + } + }, + "humidifier": { + "mode": { + "auto": "Automatinis", + "baby": "Kūdikio", + "sleep": "Miego" } } }, @@ -61,6 +84,8 @@ }, "state": { "default": { + "off": "Išjungta", + "on": "Įjungta", "unavailable": "(nepasiekiamas)", "unknown": "Nežinoma" } @@ -68,28 +93,97 @@ "supervisor": { "addon": { "dashboard": { + "action_error": { + "start_invalid_config": "Eiti į konfigūraciją" + }, + "capability": { + "apparmor": { + "title": "AppArmor" + }, + "label": { + "apparmor": "apparmor", + "auth": "auth", + "docker": "docker", + "hardware": "hardware", + "hass": "hass", + "hassio": "hassio", + "host": "host", + "host_pid": "host pid", + "ingress": "ingress", + "rating": "rating", + "stage": "stage" + }, + "role": { + "admin": "admin", + "backup": "backup", + "default": "default", + "homeassistant": "homeassistant", + "manager": "manager" + } + }, "visit_addon_page": "Apsilankykite puslapyje {name}, jei reikia daugiau detalių" + }, + "panel": { + "configuration": "Konfigūracija", + "documentation": "Dokumentacija" } }, "common": { "cancel": "Atšaukti", "close": "Užverti", - "save": "Išsaugoti" + "description": "Aprašymas", + "error": { + "unknown": "Nežinoma klaida", + "update_failed": "Atnaujinimas nepavyko" + }, + "failed_to_restart_name": "Nepavyko paleisti iš naujo {name}", + "failed_to_update_name": "Nepavyko atnaujinti {name}", + "learn_more": "Sužinokite daugiau", + "new_version_available": "Galima nauja versija", + "reload": "Perkrauti", + "restart": "Paleisti iš naujo", + "restart_name": "Iš naujo paleisti {name}", + "running_version": "Šiuo metu naudojate {version}", + "save": "Išsaugoti", + "show_more": "Rodyti daugiau informacijos apie tai", + "version": "Versija" }, "confirm": { + "restart": { + "text": "Ar jūs tikrai norite iš naujo paleisti {name}?", + "title": "Iš naujo paleisti {name}" + }, "update": { "title": "Atnaujinti {name}" } }, + "dashboard": { + "addon_new_version": "Galima nauja versija" + }, "dialog": { + "hardware": { + "attributes": "Atributai", + "device_path": "Įrenginio kelias", + "id": "ID", + "search": "Ieškoti įrenginių", + "title": "Įrenginiai" + }, "network": { "connected_to": "Prisijungta prie {ssid}", + "dhcp": "DHCP", "disabled": "Išjungta", "dns_servers": "DNS serveriai", "failed_to_change": "Nepavyko pakeisti tinklo nustatymų", + "gateway": "Šliuzo adresas", + "ip_netmask": "IP adresas/Netmask", "open": "Atidaryti", "scan_ap": "Ieškoti prieigos taškų", - "unsaved": "Turite neišsaugotų pakeitimų, ar tikrai norite išeiti?" + "static": "Statinis", + "title": "Tinklo nustatymai", + "unsaved": "Turite neišsaugotų pakeitimų, ar tikrai norite išeiti?", + "warning": "Jei keičiate „Wi-Fi“, IP ar šliuzo adresus, galite prarasti ryšį!", + "wep": "WEP", + "wpa": "wpa-psk" }, "registries": { "add_new_registry": "Pridėti naują registrą", @@ -116,46 +210,151 @@ "my": { "error_addon_not_found": "Priedas nerastas" }, + "panel": { + "dashboard": "Prietaisų skydelis", + "system": "Sistema" + }, "snapshot": { "addons": "Priedai", "could_not_create": "Nepavyko sukurti momentinės kopijos", "create": "Sukurti", + "created": "Sukurta", + "delete_snapshot_confirm": "ištrinti", "enter_password": "Įveskite slaptažodį.", + "failed_to_delete": "Ištrinti nepavyko", "folder": { - "homeassistant": "„Home Assistant“ konfigūracija" + "homeassistant": "„Home Assistant“ konfigūracija", + "share": "Dalintis" }, "folders": "Aplankai", "name": "Pavadinimas", "no_snapshots": "Dar neturite momentinių kopijų.", "password": "Slaptažodis", + "passwords_not_matching": "Slaptažodžiai nesutampa", "security": "Saugumas", + "select_type": "Pasirinkite, ką atkurti", + "selected": "{number} pasirinkta", "type": "Tipas" }, + "store": { + "no_results_found": "Rezultatų {repository} nerasta.", + "registries": "Registrai", + "repositories": "Saugyklos" + }, "system": { + "core": { + "cpu_usage": "Pagrindinio procesoriaus naudojimas", + "ram_usage": "Pagrindinės operatyviosios atminties naudojimas" + }, "host": { + "change": "Keisti", + "docker_version": "Docker versija", "hardware": "Įranga", - "import_from_usb": "Importuoti iš USB" + "import_from_usb": "Importuoti iš USB", + "ip_address": "IP adresas", + "operating_system": "Operacinė sistema" + }, + "log": { + "get_logs": "Nepavyko gauti {provider} žurnalų, {error}" + }, + "supervisor": { + "channel": "Kanalas", + "share_diagonstics_title": "Padėkite tobulinti Home Assistant", + "unhealthy_reason": { + "untrusted": "Aptiktas nepatikimas turinys" + }, + "warning": "ĮSPĖJIMAS" } } }, "ui": { + "auth_store": { + "ask": "Ar norite likti prisijungę?", + "confirm": "Taip", + "decline": "Ne" + }, "card": { "alarm_control_panel": { "arm_custom_bypass": "Individualizuotas apėjimas", - "arm_night": "Naktinė apsauga" + "arm_night": "Naktinė apsauga", + "clear_code": "Išvalyti", + "code": "Kodas" }, "camera": { "not_available": "Vaizdas negalimas" }, + "climate": { + "currently": "Šiuo metu", + "on_off": "Įjungta / išjungta" + }, + "counter": { + "actions": { + "decrement": "pamažinti", + "increment": "padidinti", + "reset": "atstatyti" + } + }, + "cover": { + "position": "Padėtis", + "tilt_position": "Pakreipimo padėtis" + }, + "fan": { + "direction": "Kryptis", + "speed": "Greitis" + }, + "humidifier": { + "mode": "Režimas" + }, + "light": { + "brightness": "Ryškumas", + "color_temperature": "Spalvos temperatūra", + "white_value": "Baltas ryškumas" + }, + "lock": { + "lock": "Užrakinti", + "unlock": "Atrakinti" + }, + "media_player": { + "media_play": "Paleisti", + "media_play_pause": "Paleisti/pristabdyti", + "sound_mode": "Garso režimas", + "source": "Šaltinis", + "text_to_speak": "Tekstas, kurį reikia kalbėti", + "turn_off": "Išjungti", + "turn_on": "Įjungti" + }, "persistent_notification": { "dismiss": "Atmesti" }, + "scene": { + "activate": "Suaktyvinti" + }, + "service": { + "run": "Paleisti" + }, + "timer": { + "actions": { + "cancel": "atšaukti", + "finish": "užbaigti", + "pause": "sustabdyti", + "start": "pradėti" + } + }, + "vacuum": { + "actions": { + "resume_cleaning": "Tęsti valymą", + "start_cleaning": "Pradėti valymą", + "turn_off": "Išjungti", + "turn_on": "Įjungti" + } + }, "weather": { "attributes": { "air_pressure": "Atmosferos slėgis", "humidity": "Santykinė oro drėgmė", "precipitation": "Krituliai", "temperature": "Temperatūra", + "visibility": "Matomumas", "wind_speed": "Vėjo greitis" }, "cardinal_direction": { @@ -168,26 +367,71 @@ "sw": "PV", "w": "V" }, - "forecast": "Prognozė" + "forecast": "Prognozė", + "high": "Aukštas", + "low": "Žemas" } }, "common": { + "back": "Atgal", "cancel": "Atšaukti", + "close": "Uždaryti", + "continue": "Tęsti", + "copied": "Nukopijuota", + "delete": "Ištrinti", + "disable": "Išjungti", + "enable": "Įgalinti", + "error_required": "Privaloma", + "leave": "Palikti", "loading": "Pakrovimas", - "save": "Išsaugoti" + "menu": "Meniu", + "next": "Kitas", + "no": "Ne", + "not_now": "Ne dabar", + "previous": "Ankstesnis", + "refresh": "Atnaujinti", + "remove": "Pašalinti", + "rename": "Pervardyti", + "save": "Išsaugoti", + "skip": "Praleisti", + "stay": "Likti", + "successfully_deleted": "Sėkmingai ištrinta", + "successfully_saved": "Sėkmingai išsaugota", + "undo": "Atšaukti", + "yes": "Taip" }, "components": { "area-picker": { + "add_dialog": { + "failed_create_area": "Nepavyko sukurti srities.", + "text": "Įveskite naujos srities pavadinimą." + }, + "add_new": "Pridėti naują sritį...", + "area": "Sritis", "no_areas": "Neturite jokių sričių", "no_match": "Nerasta atitinkančių sričių" }, + "attributes": { + "expansion_header": "Atributai" + }, "blueprint-picker": { "add_user": "Pridėti vartotoją", "remove_user": "Pašalinti vartotoją", "select_blueprint": "Pasirinkite techninį planą" }, + "data-table": { + "clear": "Išvalyti", + "hidden": "{number} paslėpta", + "no-data": "Nėra duomenų" + }, + "date-range-picker": { + "end_date": "Pabaigos data", + "select": "Pasirinkite", + "start_date": "Pradžios data" + }, "device-picker": { "device": "Įrenginys", + "no_area": "Nėra srities", "no_devices": "Neturite jokių įrenginių", "no_match": "Nerasta atitinkančių įrenginių", "show_devices": "Rodyti įrenginius" @@ -197,6 +441,10 @@ "no_match": "Nerasta atitinkančių objektų" } }, + "history_charts": { + "loading_history": "Įkeliama būsenos retrospektyva...", + "no_history_found": "Būsenos istorija nerasta." + }, "logbook": { "by_service": "pagal paslaugą", "messages": { @@ -204,6 +452,8 @@ "changed_to_state": "pakeista į {state}", "cleared_device_class": "išvalyta ({device_class} neaptikta)", "detected_device_class": "aptikta {device_class}", + "is_closing": "uždaroma", + "is_opening": "atidaroma", "rose": "pakilo", "set": "nustatyti", "turned_off": "Išjungta", @@ -225,6 +475,42 @@ "was_unsafe": "buvo nesaugus" } }, + "media-browser": { + "class": { + "album": "Albumas", + "app": "Programėlė", + "artist": "Atlikėjas", + "channel": "Kanalas", + "composer": "Kompozitorius", + "contributing_artist": "Prisidedantis atlikėjas", + "directory": "Biblioteka", + "episode": "Serija", + "genre": "Žanras", + "movie": "Filmas", + "music": "Muzika", + "playlist": "Grojaraštis", + "podcast": "Podkastas", + "season": "Sezonas", + "tv_show": "Televizijos laida", + "url": "URL", + "video": "Vaizdo įrašas" + }, + "documentation": "dokumentacija" + }, + "picture-upload": { + "label": "Paveikslėlis", + "unsupported_format": "Nepalaikomas formatas, prašome pasirinkti JPEG, PNG arba GIF atvaizdą." + }, + "related-filter-menu": { + "filter_by_area": "Filtruoti pagal sritį", + "filter_by_device": "Filtruoti pagal įrenginį", + "filtered_by_area": "sritis: {area_name}", + "filtered_by_device": "įrenginys: {device_name}" + }, + "related-items": { + "area": "Sritis", + "device": "Įrenginys" + }, "relative_time": { "duration": { "day": "{count} {count, plural,\n one {diena}\n other {dienos}\n}", @@ -232,7 +518,11 @@ "minute": "{count} {count, plural,\n one {minutė}\n other {minutės}\n}", "second": "{count} {count, plural,\n one {sekundė}\n other {sekundės}\n}", "week": "{count} {count, plural,\n one {savaitė}\n other {savaitės}\n}" - } + }, + "never": "Niekada" + }, + "service-control": { + "integration_doc": "Integracijos dokumentacija" }, "target-picker": { "add_area_id": "Pasirinkite sritį", @@ -249,12 +539,22 @@ } }, "dialogs": { + "config_entry_system_options": { + "restart_home_assistant": "Kad pakeitimai įsigaliotų, turite iš naujo paleisti Home Assistant.", + "update": "Atnaujinti" + }, "entity_registry": { + "dismiss": "Atmesti", "editor": { "device_disabled": "Šio objekto įrenginys išjungtas.", + "name": "Pavadinimas", "open_device_settings": "Atidaryti įrenginio nustatymus" }, - "faq": "dokumentacija" + "faq": "dokumentacija", + "settings": "Nustatymai" + }, + "generic": { + "close": "uždaryti" }, "helper_settings": { "counter": { @@ -264,12 +564,28 @@ "restore": "Atkurkite paskutinę žinomą reikšmę paleidus „Home Assistant“", "step": "Žingsnio dydis" }, + "input_number": { + "step": "Žingsnio dydis", + "unit_of_measurement": "Matavimo vienetai" + }, + "input_select": { + "add": "Pridėti", + "add_option": "Pridėti parinktį", + "options": "Parinktys" + }, "timer": { "duration": "Trukmė" } }, + "image_cropper": { + "crop": "Apkarpyti" + }, "more_info_control": { + "details": "Išsami informacija", "dismiss": "Atsisakyti dialogo", + "history": "Istorija", + "last_changed": "Paskutinį kartą pakeista", + "last_updated": "Paskutinį kartą atnaujinta", "remote": { "activity": "Dabartinė veikla" }, @@ -284,6 +600,24 @@ }, "updater": { "title": "Atnaujinimo instrukcijos" + }, + "vacuum": { + "fan_speed": "Ventiliatoriaus greitis", + "locate": "Surasti", + "pause": "Pristabdyti", + "return_home": "Grįžti namo", + "start": "Pradėti", + "start_pause": "Pradėti/Pristabdyti", + "status": "Būsena", + "stop": "Sustabdyti" + } + }, + "options_flow": { + "form": { + "header": "Parinktys" + }, + "success": { + "description": "Parinktys sėkmingai išsaugotos." } }, "quick-bar": { @@ -308,9 +642,48 @@ "server_control": "Serverio valdikliai", "users": "Vartotojai", "zone": "Zonos" + }, + "server_control": { + "perform_action": "{action} serveris", + "restart": "Paleisti iš naujo", + "stop": "Sustabdyti" + }, + "types": { + "reload": "Perkrauti", + "server_control": "Serveris" } }, "filter_placeholder": "Objekto filtras" + }, + "voice_command": { + "did_not_hear": "Home Assistant nieko neišgirdo", + "error": "Oi, įvyko klaida", + "how_can_i_help": "Kuo galiu padėti?", + "label": "Įveskite klausimą ir paspauskite „Enter“" + }, + "zha_device_info": { + "buttons": { + "device_children": "Peržiūrėti vaikus" + }, + "confirmations": { + "remove": "Ar jūs tikrai norite pašalinti įrenginį?" + }, + "last_seen": "Paskutinį kartą matyta", + "manuf": "sukūrė {manufacturer}", + "no_area": "Nėra srities", + "unknown": "Nežinoma" + }, + "zha_reconfigure_device": { + "attribute": "Atributas", + "button_hide": "Slėpti išsamią informaciją", + "button_show": "Rodyti išsamią informaciją", + "configuration_complete": "Įrenginio perkonfigūravimas baigtas.", + "configuration_failed": "Įrenginio perkonfigūravimas nepavyko. Žurnaluose gali būti papildomos informacijos.", + "configuring_alt": "Konfigūruojama", + "in_progress": "Įrenginys perkonfigūruojamas. Tai gali užtrukti.", + "reporting_header": "Ataskaitos", + "run_in_background": "Galite uždaryti šį dialogo langą ir perkonfigūravimas bus tęsiamas fone.", + "start_reconfiguration": "Pradėti perkonfigūravimą" } }, "duration": { @@ -320,20 +693,28 @@ "second": "{count} {count, plural,\n one {sekundė}\n other {sekundės}\n}", "week": "{count} {count, plural,\n one {savaitė}\n other {savaitės}\n}" }, + "errors": { + "supervisor": { + "ask": "Prašyti pagalbos" + } + }, "login-form": { "log_in": "Prisijungti", "password": "Slaptažodis", "remember": "Prisiminti" }, "notification_drawer": { + "click_to_configure": "Spustelėkite mygtuką, kad sukonfigūruotumėte {entity}", "close": "Uždaryti", "dismiss_all": "Atmesti viską", "empty": "Pranešimų nėra", "title": "Pranešimai" }, "notification_toast": { + "connection_lost": "Nutrūko ryšys. Jungiamasi iš naujo...", "dismiss": "Atmesti", - "started": "Home Assistant startavo" + "started": "Home Assistant startavo", + "starting": "Home Assistant kraunasi. Visos funkcijos bus prieinamos kai bus baigtas užkrovimas." }, "panel": { "config": { @@ -344,20 +725,29 @@ "areas": { "caption": "Sričių registras", "data_table": { + "area": "Sritis", "devices": "Įrenginiai" }, + "delete": { + "confirmation_title": "Ar jūs tikrai norite ištrinti šią sritį?" + }, "description": "Visų jūsų namų sričių apžvalga.", "editor": { "create": "SUKURTI", + "default_name": "Nauja sritis", "delete": "IŠTRINTI", + "unknown_error": "Nežinoma klaida", "update": "ATNAUJINTI" }, "picker": { + "create_area": "Sukurti sritį", "header": "Sričių registras", "integrations_page": "Integracijų puslapis" } }, "automation": { + "caption": "Automatika", + "description": "Sukurkite pasirinktas automatikos taisykles savo namams", "dialog_new": { "blueprint": { "use_blueprint": "Naudoti techninį planą" @@ -381,19 +771,38 @@ "header": "Veiksmai", "learn_more": "Sužinokite daugiau apie veiksmus", "name": "Veiksmas", + "type_select": "Veiksmo tipas", "type": { + "condition": { + "label": "Sąlyga" + }, "delay": { - "delay": "Uždelsta" + "delay": "Uždelsta", + "label": "Uždelsimas" + }, + "device_id": { + "action": "Veiksmas", + "extra_fields": { + "code": "Kodas", + "humidity": "Drėgmė", + "mode": "Režimas", + "value": "Vertė" + }, + "label": "Įrenginys" }, "event": { "event": "Įvykiai", "label": "Sukurti įvykį" }, + "scene": { + "label": "Suaktyvinti sceną" + }, "wait_template": { "label": "Laukti" } } }, + "alias": "Pavadinimas", "blueprint": { "blueprint_to_use": "Naudotini techniniai planai", "header": "Techninis planas", @@ -401,14 +810,31 @@ "no_inputs": "Šis techninis planas neturi jokių įvesčių." }, "conditions": { + "add": "Pridėti sąlygą", "delete": "Ištrinti", "duplicate": "Dubliuoti", + "header": "Sąlygos", + "introduction": "Sąlygos yra neprivalomos ir neleis automatikos scenarijams veikti, nebent bus įvykdytos visos sąlygos.", "learn_more": "Sužinokite daugiau apie sąlygas", "name": "Sąlyga", "type": { + "and": { + "label": "Ir" + }, + "device": { + "condition": "Sąlyga", + "extra_fields": { + "for": "Trukmė" + }, + "label": "Įrenginys" + }, "numeric_state": { + "above": "Daugiau", "below": "žemiau" }, + "or": { + "label": "Arba" + }, "sun": { "after": "Po:", "before": "Prieš:", @@ -424,7 +850,13 @@ "before": "Prieš", "label": "Laikas", "weekdays": { - "mon": "Pirmadienis" + "fri": "Penktadienis", + "mon": "Pirmadienis", + "sat": "Šeštadienis", + "sun": "Sekmadienis", + "thu": "Ketvirtadienis", + "tue": "Antradienis", + "wed": "Trečiadienis" } }, "zone": { @@ -434,19 +866,37 @@ } }, "copy_to_clipboard": "Kopijuoti į iškarpinę", + "default_name": "Naujas automatikos scenarijus", + "description": { + "label": "Aprašymas", + "placeholder": "Neprivalomas aprašymas" + }, "edit_ui": "Redaguoti naudojant vartotojo sąsają", "edit_yaml": "Redaguoti kaip YAML", + "introduction": "Naudokite automatikos scenarijus, kad atgaivintumėte savo namus.", "load_error_not_editable": "Redagavimas leidžiamas tik automatizavimai, esantys automations.yaml", + "modes": { + "documentation": "automatikos dokumentacija", + "label": "Režimas" + }, + "save": "Išsaugoti", "triggers": { "add": "Pridėti trigerį", "delete": "Ištrinti", "delete_confirm": "Ar tikrai norite tai ištrinti?", "duplicate": "Pasikartojantys", + "header": "Trigeriai", "introduction": "Trigeriai yra tai, kas pradeda automatizavimo taisyklės apdorojimą. Tai pačiai taisyklei galima nurodyti kelis aktyviklius. Kai tik įsijungs trigeris, Home Assistant patikrins sąlygas, jei tokių yra, ir iškviečia veiksmą.", "learn_more": "Sužinokite daugiau apie trigerius", "name": "Trigeris", "type_select": "Trigerio tipas", "type": { + "device": { + "extra_fields": { + "for": "Trukmė" + }, + "label": "Įrenginys" + }, "event": { "context_user_pick": "Pridėti vartotoją", "context_user_picked": "Vartotojo įvykis", @@ -481,6 +931,7 @@ }, "state": { "from": "Iš", + "label": "Būsena", "to": "Kam" }, "sun": { @@ -490,28 +941,55 @@ "sunrise": "Saulėtekis", "sunset": "Saulėlydis" }, + "tag": { + "label": "Žyma" + }, + "template": { + "label": "Šablonas", + "value_template": "Vertės šablonas" + }, "time_pattern": { "hours": "Valandos", "label": "Laiko modelis", "minutes": "Minutes", "seconds": "Sekundės" }, + "time": { + "at": "Tuo metu", + "label": "Laikas" + }, "webhook": { "label": "Webhook", "webhook_id": "Webhook ID" }, "zone": { "event": "Įvykis", + "label": "Zona", + "leave": "Palieka", "zone": "Vieta" } }, "unsupported_platform": "Nėra UI palaikymo platformai: {platform}" - } + }, + "unsaved_confirm": "Turite neišsaugotų pakeitimų, ar tikrai norite išeiti?" }, "picker": { + "add_automation": "Pridėti automatikos scenarijų", + "header": "Automatikos redaktorius", + "introduction": "Automatikos redaktorius leidžia kurti ir redaguoti automatikos scenarijus. Spustelėkite toliau pateiktą nuorodą, kad perskaitytumėte instrukcijas ir įsitikintumėte, jog tinkamai sukonfigūravote Home Assistant.", "learn_more": "Sužinokite daugiau apie automatizavimą", "no_automations": "Redaguojamas automatizavimas nerastas", - "only_editable": "Redagavimas leidžiamas tik automatizavimai, esantys automations.yaml" + "only_editable": "Redagavimas leidžiamas tik automatizavimai, esantys automations.yaml", + "pick_automation": "Pasirinkite redaguojamą automatikos scenarijų" + }, + "thingtalk": { + "link_devices": { + "header": "Puiku! Dabar turime susieti kai kuriuos įrenginius" + }, + "task_selection": { + "error_empty": "Įveskite komandą arba bakstelėkite praleisti.", + "for_example": "Pavyzdžiui:" + } } }, "blueprint": { @@ -548,31 +1026,167 @@ }, "cloud": { "account": { - "connected": "Prisijungęs", - "google": { - "devices_pin": "Įrenginio saugos Pin kodas", - "security_devices": "Apsaugoti įrenginiai" + "alexa": { + "config_documentation": "Konfigūracijos dokumentacija", + "disable": "išjungti", + "enable": "įgalinti", + "title": "Alexa" }, - "not_connected": "Neprisijungęs" + "connected": "Prisijungęs", + "connecting": "Jungiamasi...", + "google": { + "config_documentation": "Konfigūracijos dokumentacija", + "devices_pin": "Įrenginio saugos Pin kodas", + "enter_pin_error": "Nepavyko išsaugoti PIN kodo:", + "not_configured_title": "Google Assistant nėra aktyvuotas", + "security_devices": "Apsaugoti įrenginiai", + "title": "Google Assistant" + }, + "integrations": "Integracijos", + "manage_account": "Tvarkyti paskyrą", + "not_connected": "Neprisijungęs", + "remote": { + "certificate_info": "Sertifikato informacija", + "connected": "Prisijungęs", + "link_learn_how_it_works": "Sužinokite, kaip tai veikia", + "not_connected": "Neprisijungęs", + "remote_enabled": { + "caption": "Automatiškai prisijungti" + }, + "title": "Nuotolinis valdymas" + }, + "sign_out": "Atsijungti", + "tts": { + "default_language": "Numatytoji kalba", + "dialog": { + "target_browser": "Naršyklė" + }, + "female": "Moteris", + "male": "Vyras", + "title": "Tekstas į kalbą", + "try": "Bandyti" + }, + "webhooks": { + "loading": "Įkeliama...", + "manage": "Tvarkyti", + "title": "Webhooks" + } + }, + "alexa": { + "manage_domains": "Tvarkyti domenus", + "title": "Alexa" + }, + "description_login": "Prisijungta kaip {email}", + "description_not_login": "Neprisijungęs", + "dialog_certificate": { + "certificate_expiration_date": "Sertifikato galiojimo data:", + "certificate_information": "Sertifikato informacija", + "close": "Uždaryti", + "fingerprint": "Sertifikato piršto atspaudas:" + }, + "dialog_cloudhook": { + "close": "Uždaryti", + "copied_to_clipboard": "Nukopijuota į iškarpinę", + "view_documentation": "Peržiūrėti dokumentaciją" + }, + "forgot_password": { + "check_your_email": "Patikrinkite savo el. paštą, kuriame rasite instrukcijas, kaip iš naujo nustatyti slaptažodį.", + "email": "El. paštas", + "email_error_msg": "Neteisingas el. pašto adresas", + "instructions": "Įveskite savo el. pašto adresą ir mes atsiųsime jums nuorodą, kad galėtumėte iš naujo nustatyti savo slaptažodį.", + "send_reset_email": "Siųsti atstatymo el. laišką", + "subtitle": "Pamiršote savo slaptažodį", + "title": "Pamiršau slaptažodį" + }, + "google": { + "manage_domains": "Tvarkyti domenus", + "sync_to_google": "Sinchronizuojami pakeitimai su Google.", + "title": "Google Assistant" + }, + "login": { + "alert_email_confirm_necessary": "Prieš prisijungdami turite patvirtinti savo el. pašto adresą.", + "alert_password_change_required": "Prieš prisijungdami turite pakeisti slaptažodį.", + "dismiss": "Atmesti", + "email": "El. paštas", + "email_error_msg": "Neteisingas el. paštas", + "forgot_password": "Pamiršote slaptažodį?", + "learn_more_link": "Sužinokite daugiau apie Home Assistant Cloud", + "password": "Slaptažodis", + "password_error_msg": "Slaptažodis turi turėti bent 8 simbolius", + "sign_in": "Prisijungti", + "start_trial": "Pradėti nemokamą 1 mėnesio bandomąją versiją", + "trial_info": "Mokėjimo informacijos pateikti nereikia" + }, + "register": { + "account_created": "Paskyra sukurta! Patikrinkite savo el. paštą, kuriame rasite instrukcijas, kaip aktyvuoti paskyrą.", + "create_account": "Sukurti paskyrą", + "email_address": "El. pašto adresas", + "email_error_msg": "Neteisingas el. pašto adresas", + "feature_amazon_alexa": "Integracija su Amazon Alexa", + "feature_google_home": "Integracija su Google Assistant", + "information4": "Registruodami paskyrą sutinkate su šiomis sąlygomis ir nuostatomis.", + "link_privacy_policy": "Privatumo politika", + "link_terms_conditions": "Terminai ir sąlygos", + "password": "Slaptažodis", + "password_error_msg": "Slaptažodis turi turėti bent 8 simbolius", + "resend_confirm_email": "Pakartotinai išsiųsti patvirtinimo el. laišką", + "title": "Registruoti paskyrą" } }, "core": { - "caption": "Bendra" + "caption": "Bendra", + "description": "Vienetų sistema, vieta, laiko juosta ir kiti bendrieji parametrai", + "section": { + "core": { + "analytics": { + "learn_more": "Kaip mes tvarkome jūsų duomenis", + "preference": { + "diagnostics": { + "description": "Bendrinkite trigdžių ataskaitas, kai įvyksta netikėtų klaidų.", + "title": "Diagnostika" + }, + "statistics": { + "title": "Naudojimo statistika" + }, + "usage_supervisor": { + "description": "Pavadinimai, versijos ir galimybės." + }, + "usage": { + "description": "Pavadinimai ir versijos informacija.", + "title": "Naudotos integracijos" + } + } + }, + "core_config": { + "latitude": "Platuma", + "longitude": "Ilguma", + "save_button": "Išsaugoti" + }, + "header": "Bendroji konfigūracija", + "introduction": "Konfigūracijos keitimas gali būti varginantis procesas. Taip, mes žinome! Todėl šiame skyriuje bandysime šiek tiek palengvinti jūsų gyvenimą." + } + } }, "customize": { + "caption": "Pritaikymai", "picker": { "documentation": "Tinkinimo dokumentacija", "header": "Pritaikymas" + }, + "warning": { + "include_link": "įtraukti customize.yaml" } }, "devices": { "automation": { "actions": { + "caption": "Kai kažkas suveikia...", "no_actions": "Nėra veiksmų", "unknown_action": "Nežinomas veiksmas" }, "automations": "Automatizavimas", "conditions": { + "caption": "Daryti ką nors tik tuo atveju, jei...", "no_conditions": "Nėra sąlygų", "unknown_condition": "Nežinoma sąlyga" }, @@ -580,11 +1194,13 @@ "no_automations": "Nėra automatizavimų", "no_device_automations": "Šiam įrenginiui nėra automatizavimo įrankių", "triggers": { + "caption": "Daryti ką nors, kai...", "no_triggers": "Nėra paleidiklių", "unknown_trigger": "Nežinomas paleidiklis" }, "unknown_automation": "Nežinomas automatizavimas" }, + "caption": "Įrenginiai", "confirm_delete": "Ar tikrai norite ištrinti šį įrenginį?", "data_table": { "area": "Sritis", @@ -628,7 +1244,8 @@ "create_disable": "Negalima sukurti skripto su išjungtu įrenginiu" }, "unknown_error": "Nežinoma klaida", - "unnamed_device": "Bevardis įrenginys" + "unnamed_device": "Bevardis įrenginys", + "update_device_error": "Nepavyko atnaujinti įrenginio" }, "entities": { "caption": "Subjektų registras", @@ -639,38 +1256,160 @@ }, "header": "Subjektų registras", "headers": { - "area": "Sritis" + "area": "Sritis", + "status": "Būsena" }, - "introduction2": "Naudokite subjekto registrą, kad perrašytumėte pavadinimą, pakeiskite subjekto ID arba pašalintumėte įrašą iš namų asistento. Atminkite, kad pašalindami registro įrašą tai nepanaikins pačio subjekto. Norėdami tai padaryti, sekite toliau pateiktą nuorodą ir pašalinkite ją iš integracijos puslapio." + "introduction2": "Naudokite subjekto registrą, kad perrašytumėte pavadinimą, pakeiskite subjekto ID arba pašalintumėte įrašą iš namų asistento. Atminkite, kad pašalindami registro įrašą tai nepanaikins pačio subjekto. Norėdami tai padaryti, sekite toliau pateiktą nuorodą ir pašalinkite ją iš integracijos puslapio.", + "selected": "{number} pasirinkta", + "status": { + "disabled": "Išjungta", + "ok": "Gerai" + } } }, + "filtering": { + "clear": "Išvalyti" + }, "header": "Konfigūruoti Home Assistant", "helpers": { + "dialog": { + "create": "Sukurti" + }, + "picker": { + "headers": { + "editable": "Redaguojamas", + "name": "Pavadinimas", + "type": "Tipas" + } + }, "types": { "counter": "Skaitiklis", + "input_boolean": "Perjungėjas", + "input_datetime": "Data ir (arba) laikas", + "input_number": "Skaičius", + "input_select": "Išskleidžiamas meniu", + "input_text": "Tekstas", "timer": "Laikmatis" } }, "info": { + "caption": "Informacija", "copy_github": "GitHub'ui", - "copy_raw": "Neapdorotas tekstas" + "copy_raw": "Neapdorotas tekstas", + "description": "Versija, sistemos būklė ir nuorodos į dokumentaciją", + "home_assistant_logo": "Home Assistant logotipas", + "server": "serveris", + "setup_time": "Nustatyti laiką", + "source": "Šaltinis:", + "system_health": { + "manage": "Tvarkyti", + "more_info": "daugiau informacijos" + } }, "integrations": { + "add_integration": "Pridėti integraciją", + "caption": "Integracijos", "config_entry": { + "check_the_logs": "Patikrinkite žurnalus", + "configure": "Konfigūruoti", + "delete": "Ištrinti", + "documentation": "Dokumentacija", + "firmware": "Programinė įranga: {versija}", "hub": "Prijungtas per", + "logs": "žurnalai", + "manuf": "sukūrė {manufacturer}", "no_area": "Nėra srities", - "provided_by_custom_integration": "Teikiama naudojant pasirinktinę integraciją" + "not_loaded": "Nepakrauta", + "provided_by_custom_integration": "Teikiama naudojant pasirinktinę integraciją", + "rename": "Pervardyti", + "state": { + "failed_unload": "Įkrauti nepavyko", + "loaded": "Įkrauta", + "migration_error": "Perkėlimo klaida", + "not_loaded": "Nepakrauta" + } }, "config_flow": { "aborted": "Nutraukta", "close": "Uždaryti", + "external_step": { + "description": "Šiam veiksmui atlikti reikia apsilankyti išorinėje svetainėje.", + "open_site": "Atidaryti svetainę" + }, "finish": "Baigti", + "next": "Kitas", "submit": "Pateikti" }, - "details": "Integravimo informacija" + "configured": "Sukonfigūruota", + "details": "Integravimo informacija", + "disable": { + "show": "Rodyti" + }, + "discovered": "Atrasta", + "home_assistant_website": "Home Assistant svetainė", + "ignore": { + "ignore": "Nepaisyti", + "ignored": "Nepaisoma" + }, + "integration": "integracija" }, "introduction": "Šiame vaizde galima sukonfigūruoti savo komponentus ir Home Assistant. Dar ne viską galima sukonfigūruoti iš vartotojo sąsajos (UI ), tačiau mes prie to dirbame.", + "logs": { + "caption": "Žurnalai", + "clear": "Išvalyti", + "description": "Peržiūrėti Home Assistant žurnalus", + "level": { + "info": "INFO" + }, + "loading_log": "Įkeliamas klaidų žurnalas...", + "no_errors": "Apie klaidas nepranešta", + "refresh": "Atnaujinti" + }, + "lovelace": { + "dashboards": { + "caption": "Prietaisų skydeliai", + "confirm_delete": "Ar jūs tikrai norite ištrinti šį prietaisų skydelį?", + "detail": { + "create": "Sukurti", + "delete": "Ištrinti", + "dismiss": "Uždaryti", + "edit_dashboard": "Redaguoti prietaisų skydelį", + "icon": "Piktograma", + "new_dashboard": "Pridėti naują prietaisų skydelį", + "require_admin": "Tik administratoriui", + "title": "Pavadinimas", + "update": "Atnaujinti", + "url": "URL", + "url_error_msg": "URL turi būti raidė - ir jame negali būti tarpų ar specialiųjų simbolių, išskyrus _ ir -" + }, + "picker": { + "add_dashboard": "Pridėti prietaisų skydelį", + "headers": { + "filename": "Failo pavadinimas", + "title": "Pavadinimas" + }, + "open": "Atidaryti" + } + }, + "resources": { + "caption": "Ištekliai", + "picker": { + "headers": { + "type": "Tipas", + "url": "URL" + } + }, + "types": { + "css": "Stilių rinkinys", + "html": "HTML (nebenaudojama)", + "js": "JavaScript failas (nebenaudojama)", + "module": "JavaScript modulis" + } + } + }, "mqtt": { + "button": "Konfigūruoti", + "publish": "Paskelbti", "title": "MQTT" }, "ozw": { @@ -679,6 +1418,7 @@ "wakeup_instructions": "Pažadinimo instrukcijos" }, "navigation": { + "network": "Tinklas", "node": { "config": "Konfigūracija", "dashboard": "Valdymo skydas" @@ -687,6 +1427,10 @@ "network_status": { "unknown": "Nežinoma" }, + "network": { + "header": "Tinklo valdymas", + "introduction": "Tvarkykite viso tinklo funkcijas." + }, "node_config": { "header": "Mazgo konfigūracija", "help_source": "Konfigūracijos parametrų aprašymai ir žinyno tekstas pateikiami OpenZWave projekto.", @@ -694,17 +1438,38 @@ }, "node_metadata": { "product_manual": "Gaminio vadovas" + }, + "nodes_table": { + "id": "ID", + "manufacturer": "Gamintojas", + "model": "Modelis" + }, + "refresh_node": { + "step": "Žingsnis" + }, + "services": { + "cancel_command": "Atšaukti komandą" } }, "person": { + "add_person": "Pridėti asmenį", "caption": "Asmenys", + "confirm_delete": "Ar jūs tikrai norite ištrinti šį asmenį?", + "confirm_delete2": "Visi šiam asmeniui priklausantys įrenginiai taps nepriskirti.", + "create_person": "Sukurti asmenį", "detail": { + "admin": "Administratorius", "allow_login": "Leisti asmeniui prisijungti", "confirm_delete_user": "Ar tikrai norite panaikinti {name} vartotojo abonementą? Jūs vis dar galėsite sekti vartotoją, bet asmuo nebegalės prisijungti.", + "create": "Sukurti", + "delete": "Ištrinti", "device_tracker_intro": "Pasirinkite įrenginius, priklausančius šiam asmeniui.", "device_tracker_pick": "Pasirinkite įrenginį, kurį norite stebėti", "device_tracker_picked": "Stebėti įrenginį", - "name": "Vardas" + "linked_user": "Susietas vartotojas", + "name": "Vardas", + "new_person": "Naujas asmuo", + "update": "Atnaujinti" }, "learn_more": "Sužinokite daugiau apie žmones", "person_not_found": "Nepavyko rasti asmens, kurį bandėte redaguoti.", @@ -718,52 +1483,117 @@ "delete": "Pašalinti įrenginį", "header": "Įrenginiai" }, + "icon": "Piktograma", "load_error_not_editable": "Redaguoti galima tik scenas, esančias scenes.yaml." }, "picker": { + "add_scene": "Pridėti sceną", + "delete_confirm": "Ar jūs tikrai norite ištrinti šią sceną?", + "delete_scene": "Ištrinti sceną", + "edit_scene": "Redaguoti sceną", "learn_more": "Sužinokite daugiau apie scenas", "no_scenes": "Neradome jokių redaguojamų scenų", - "only_editable": "Redaguoti galima tik scenas, esančias scenes.yaml." + "only_editable": "Redaguoti galima tik scenas, esančias scenes.yaml.", + "pick_scene": "Pasirinkite sceną, kurią norite redaguoti", + "show_info_scene": "Rodyti informaciją apie sceną" } }, "script": { + "caption": "Scenarijai", + "description": "Atlikite veiksmų seką", "editor": { - "save_script": "Įrašyti scenarijų" + "default_name": "Naujas scenarijus", + "delete_confirm": "Ar tikrai norite ištrinti šį scenarijų?", + "header": "Scenarijus: {name}", + "modes": { + "label": "Režimas" + }, + "save_script": "Įrašyti scenarijų", + "sequence": "Seka", + "sequence_sentence": "Šio scenarijaus veiksmų seka." }, "picker": { + "add_script": "Pridėti scenarijų", "duplicate": "Dubliuoti", "duplicate_script": "Dubliuoti skriptą", "run_script": "Vykdyti scenarijų" } }, "server_control": { + "description": "Paleisti iš naujo ir sustabdyti Home Assistant serverį", "section": { "reloading": { - "scene": "Perkraukite scenas" + "homekit": "HomeKit", + "person": "Žmonės", + "reload": "{domain}", + "scene": "Perkraukite scenas", + "telegram": "Telegram pranešimo paslaugos", + "zone": "Zonos" + }, + "server_management": { + "confirm_stop": "Ar jūs tikrai norite sustabdyti Home Assistant?", + "heading": "Serverio valdymas", + "introduction": "Valdykite Home Assistant serverį... iš Home Assistant." + }, + "validation": { + "heading": "Konfigūracijos patvirtinimas", + "introduction": "Patikrinkite konfigūraciją, jei neseniai atlikote kai kuriuos konfigūracijos pakeitimus ir įsitikinkite, kad visa konfigūracija teisinga.", + "invalid": "Konfigūracija neteisinga", + "valid": "Konfigūracija teisinga!" } } }, "tag": { + "add_tag": "Pridėti žymą", + "caption": "Žymos", "detail": { "companion_apps": "papildomos programos", + "create": "Sukurti", + "create_and_write": "Sukurti ir įrašyti", + "delete": "Ištrinti", + "description": "Aprašymas", + "tag_id": "Žymos ID", + "update": "Atnaujinti", "usage": "Nuskaitant žymą galima suaktyvinti automatizavimą, galite naudoti NFC žymes, QR kodus ar bet kokios kitos rūšies žymą. Naudokite mūsų {companion_link}, jei norite parašyti šią žymą į programuojamą NFC žymę arba sukurti QR kodą žemiau." }, - "learn_more": "Sužinokite daugiau apie žymas" + "edit": "Redaguoti", + "learn_more": "Sužinokite daugiau apie žymas", + "no_tags": "Nėra žymų" }, "users": { "add_user": { "caption": "Pridėti vartotoją", "create": "Sukurti", - "password": "Slaptažodis" + "password": "Slaptažodis", + "password_not_match": "Slaptažodžiai nesutampa" }, + "caption": "Vartotojai", + "description": "Tvarkykite Home Assistant vartotojų paskyras", "editor": { + "activate_user": "Suaktyvinti vartotoją", + "active": "Aktyvus", "active_tooltip": "Tikrina, ar vartotojas gali prisijungti", + "admin": "Administratorius", "caption": "Peržiūrėti vartotoją", + "change_password": "Pakeisti slaptažodį", + "confirm_user_deletion": "Ar jūs tikrai norite ištrinti {vardą}?", + "deactivate_user": "Išjungti vartotoją", + "delete_user": "Ištrinti vartotoją", + "group": "Grupė", + "id": "ID", + "name": "Rodomas pavadinimas", + "new_password": "Naujas slaptažodis", + "owner": "Savininkas", + "password_changed": "Slaptažodis sėkmingai pakeistas", + "system_generated": "Sugeneruota sistemos", + "unnamed_user": "Bevardis vartotojas", + "update_user": "Atnaujinti", "username": "Vartotojo vardas" }, "picker": { "add_user": "Pridėti vartotoją", "headers": { + "group": "Grupė", "is_active": "Aktyvus", "is_owner": "Savininkas", "username": "Vartotojo vardas" @@ -775,6 +1605,10 @@ "add_device_page": { "spinner": "Ieškoma ZHA Zigbee įrenginių..." }, + "button": "Konfigūruoti", + "configuration_page": { + "update_button": "Atnaujinti konfigūraciją" + }, "device_pairing_card": { "CONFIGURED": "Konfigūracija baigta", "CONFIGURED_status_text": "Inicijuojama", @@ -783,17 +1617,116 @@ "INTERVIEW_COMPLETE_status_text": "Konfigūruojama", "PAIRED": "Rastas įrenginys" }, + "group_binding": { + "bind_button_label": "Susieti grupę", + "header": "Grupės susiejimas", + "introduction": "Susieti ir atsieti grupes." + }, "groups": { - "add_group": "Pridėti grupę" + "add_group": "Pridėti grupę", + "add_members": "Pridėti narių", + "caption": "Grupės", + "create": "Sukurti grupę", + "group_details": "Čia pateikiama visa informacija apie pasirinktą Zigbee grupę.", + "group_id": "Grupės ID", + "group_info": "Grupės informacija", + "group_name_placeholder": "Grupės pavadinimas", + "group_not_found": "Grupė nerasta!", + "groups": "Grupės", + "members": "Nariai", + "remove_members": "Pašalinti narius" + }, + "network": { + "caption": "Tinklas" }, "visualization": { "caption": "Vizualizacija", "header": "Tinklo vizualizacija" } }, - "zwave": { + "zone": { + "add_zone": "Pridėti zoną", + "caption": "Zonos", + "confirm_delete": "Ar jūs tikrai norite ištrinti šią zoną?", + "create_zone": "Sukurti zoną", + "detail": { + "create": "Sukurti", + "delete": "Ištrinti", + "icon": "Piktograma", + "icon_error_msg": "Piktograma turi būti tokio formato: „prefix: iconname“, pvz .: „mdi: home“", + "latitude": "Platuma", + "longitude": "Ilguma", + "new_zone": "Nauja zona", + "passive": "Pasyvus", + "radius": "Spindulys", + "required_error_msg": "Šis laukas yra privalomas", + "update": "Atnaujinti" + } + }, + "zwave_js": { + "common": { + "reconfigure_server": "Iš naujo sukonfigūruoti serverį" + }, + "dashboard": { + "driver_version": "Tvarkyklės versija", + "dump_not_ready_confirm": "Atsisiųsti", + "server_version": "Serverio versija" + }, + "device_info": { + "device_config": "Konfigūruoti įrenginį" + }, + "logs": { + "log_level": "Žurnalo lygis", + "log_level_changed": "Žurnalo lygis pakeistas į: {level}" + }, + "navigation": { + "logs": "Žurnalai" + }, + "network_status": { + "connected": "Prisijungęs", + "connecting": "Jungiasi", + "unknown": "Nežinoma" + }, "node_config": { - "set_config_parameter": "Nustatykite parametrą „Config“" + "attribution": "Įrenginio konfigūracijos parametrus ir aprašymus pateikia {device_database}", + "battery_device_notice": "Įrenginiai su baterijomis turi būti pažadinti, kad būtų galima atnaujinti jų konfigūraciją. Instrukcijas, kaip pažadinti įrenginį, rasite įrenginio vadove.", + "error_device_not_found": "Įrenginys nerastas", + "set_param_accepted": "Parametras buvo atnaujintas.", + "set_param_error": "Įvyko klaida.", + "set_param_queued": "Parametro pakeitimas buvo įrašytas į eilę ir bus atnaujintas, kai įrenginys pabus." + }, + "node_status": { + "alive": "Gyvas", + "asleep": "Miega", + "awake": "Budi", + "dead": "Miręs", + "unknown": "Nežinoma" + } + }, + "zwave": { + "button": "Konfigūruoti", + "common": { + "wakeup_interval": "Žadinimo intervalas" + }, + "node_config": { + "false": "Netiesa", + "seconds": "sekundės", + "set_config_parameter": "Nustatykite parametrą „Config“", + "set_wakeup": "Nustatyti žadinimo intervalą", + "true": "Tiesa" + }, + "node_management": { + "add_to_group": "Pridėti prie grupės", + "group": "Grupė", + "protection": "Apsauga", + "set_protection": "Nustatyti apsaugą" + }, + "services": { + "save_config": "Išsaugoti konfigūraciją", + "soft_reset": "Švelnus nustatymas iš naujo", + "start_network": "Paleisti tinklą", + "stop_network": "Sustabdyti tinklą", + "test_network": "Tikrinti tinklą" } } }, @@ -807,23 +1740,34 @@ "developer-tools": { "tabs": { "events": { + "documentation": "Įvykių dokumentacija", "title": "Įvykiai" }, "services": { + "no_template_ui_support": "Vartotojo sąsaja nepalaiko šablonų, bet jūs vis tiek galite naudoti YAML redaktorių.", "title": "Paslaugos" }, "states": { "last_changed": "Pakeista", "last_updated": "Atnaujinta", + "more_info": "Daugiau informacijos", "title": "Būsenos" }, "templates": { + "domain": "Domenas", "result_type": "Rezultato tipas", "time": "Šis šablonas atnaujinamas kiekvienos minutės pradžioje.", "title": "Šablonas" } } }, + "error": { + "go_back": "Grįžti atgal", + "supervisor": { + "ask": "Prašyti pagalbos", + "system_health": "Patikrinti sistemos būklę" + } + }, "history": { "ranges": { "today": "Šiandien", @@ -837,9 +1781,21 @@ }, "lovelace": { "cards": { + "confirm_delete": "Ar tikrai norite ištrinti šią kortelę?", "empty_state": { "go_to_integrations_page": "Į integracijų puslapį", "title": "Sveiki sugrįžę namo" + }, + "picture-elements": { + "hold": "Laikykite:", + "navigate_to": "Eikite į {location}", + "tap": "Palieskite:", + "toggle": "Perjungti {name}" + }, + "shopping-list": { + "add_item": "Pridėti elementą", + "checked_items": "Patikrinti elementai", + "clear_items": "Išvalyti patikrintus elementus" } }, "editor": { @@ -852,14 +1808,37 @@ "none": "Nėra veiksmų", "toggle": "Perjungti", "url": "Internetinis adresas" - } + }, + "navigation_path": "Navigacijos kelias", + "url_path": "URL kelias" }, "card": { "button": { - "default_action_help": "Numatytasis veiksmas priklauso nuo objekto galimybių, jis bus perjungtas arba bus rodoma daugiau informacijos." + "default_action_help": "Numatytasis veiksmas priklauso nuo objekto galimybių, jis bus perjungtas arba bus rodoma daugiau informacijos.", + "name": "Mygtukas" + }, + "calendar": { + "description": "Kalendoriaus kortelėje rodomas kalendorius, įskaitant dienos, savaitės ir sąrašo rodinius", + "inital_view": "Pradinis vaizdas", + "name": "Kalendorius", + "views": { + "dayGridDay": "Diena", + "dayGridMonth": "Mėnuo", + "listWeek": "Sąrašas" + } + }, + "conditional": { + "card": "Kortelė" + }, + "config": { + "optional": "Neprivaloma", + "required": "Privaloma" }, "entities": { "entity_row_editor": "Objekto eilučių rengyklė", + "entity_row": { + "button": "Mygtukas" + }, "secondary_info_values": { "brightness": "Ryškumas", "entity-id": "Objekto ID", @@ -869,14 +1848,56 @@ "none": "Nėra antrinės informacijos", "position": "Padėtis", "tilt-position": "Pakreipimo padėtis" + }, + "show_header_toggle": "Rodyti antraštės perjungiklį?" + }, + "gauge": { + "severity": { + "green": "Žalia", + "red": "Raudona", + "yellow": "Geltona" } }, + "generic": { + "icon": "Piktograma", + "icon_height": "Piktogramos aukštis", + "image": "Vaizdo kelias", + "no_theme": "Nėra temos", + "refresh_interval": "Atnaujinimo intervalas", + "show_icon": "Rodyti piktogramą?", + "show_name": "Rodyti pavadinimą?", + "theme": "Tema", + "title": "Pavadinimas", + "unit": "Vienetas" + }, + "glance": { + "columns": "Stulpeliai" + }, + "grid": { + "description": "Tinklelio kortelė leidžia rodyti kelias korteles tinklelyje.", + "name": "Tinklelis" + }, "logbook": { "description": "Veiksmų žurnalo kortelėje rodomas įvykių sąrašas.", "name": "Veiksmų žurnalas" }, "picture-glance": { "state_entity": "Būsenos objektas" + }, + "plant-status": { + "name": "Augalo būklė" + }, + "sensor": { + "name": "Jutiklis" + }, + "shopping-list": { + "name": "Pirkinių sąrašas" + }, + "thermostat": { + "name": "Termostatas" + }, + "weather-forecast": { + "name": "Orų prognozė" } }, "common": { @@ -891,12 +1912,16 @@ "edit_card": { "add": "Pridėti kortelę", "clear": "Išvalyti", + "confirm_cancel": "Ar jūs tikrai norite atšaukti?", "delete": "Ištrinti", "edit": "Redaguoti", + "header": "Kortelės konfigūracija", "move": "Perkelti", "move_after": "Perkelti kortelę po", "move_before": "Perkelti kortelę prieš", - "pick_card": "Pasirinkite kortelę, kurią norite pridėti." + "pick_card": "Pasirinkite kortelę, kurią norite pridėti.", + "toggle_editor": "Perjungti redaktorių", + "unsaved_changes": "Turite neišsaugotų pakeitimų" }, "edit_lovelace": { "edit_title": "Redaguoti pavadinimą" @@ -930,6 +1955,7 @@ "raw_editor": "Konfigūracijos redaktorius" }, "migrate": { + "header": "Konfigūracija nesuderinama", "migrate": "Perkelti konfigūraciją" }, "raw_editor": { @@ -945,6 +1971,7 @@ }, "save_config": { "cancel": "Nesvarbu", + "empty_config": "Pradėti nuo tuščio prietaisų skydelio", "header": "Valdykite savo „Lovelace“ vartotojo sąsają", "para_sure": "Ar tikrai norite kontroliuoti savo vartotojo sąsają?", "save": "Kontroliuoti" @@ -955,6 +1982,11 @@ "header": "Antraštės rengyklė", "row": "Objekto eilučių rengyklė" } + }, + "view": { + "panel_mode": { + "warning_multiple_cards": "Šiame rodinyje yra daugiau nei viena kortelė, tačiau skydelio rodinyje gali būti rodoma tik 1 kortelė." + } } }, "menu": { @@ -964,6 +1996,7 @@ "help": "Pagalba", "start_conversation": "Pradėti pokalbį" }, + "reload_lovelace": "Perkrauti vartotojo sąsają", "unused_entities": { "domain": "Domenas", "entity": "Subjektas", @@ -981,8 +2014,13 @@ "empty": "Neturite jokių pranešimų", "playback_title": "Pranešimų atkūrimas" }, + "my": { + "error": "Įvyko nežinoma klaida" + }, "page-authorize": { + "abort_intro": "Prisijungimas nutrauktas", "form": { + "error": "Klaida: {error}", "providers": { "command_line": { "error": { @@ -1002,11 +2040,97 @@ } } } + }, + "homeassistant": { + "error": { + "invalid_auth": "Neteisingas vartotojo vardas arba slaptažodis" + }, + "step": { + "init": { + "data": { + "password": "Slaptažodis", + "username": "Vartotojo vardas" + } + } + } + }, + "legacy_api_password": { + "error": { + "invalid_auth": "Neteisingas API slaptažodis" + }, + "step": { + "init": { + "data": { + "password": "API slaptažodis" + } + } + } + }, + "trusted_networks": { + "step": { + "init": { + "data": { + "user": "Vartotojas" + } + } + } + } + }, + "start_over": "Pradėti iš naujo", + "unknown_error": "Kažkas nutiko ne taip", + "working": "Prašome palaukti" + }, + "initializing": "Inicijuojama", + "logging_in_with": "Prisijungiama naudojant **{authProviderName}**.", + "pick_auth_provider": "Arba prisijunkite naudodami" + }, + "page-demo": { + "cards": { + "demo": { + "demo_by": "pateikė {name}", + "learn_more": "Sužinokite daugiau apie Home Assistant", + "next_demo": "Kita demonstracija" + } + }, + "config": { + "arsaboo": { + "labels": { + "activity": "Veikla", + "entertainment": "Pramogos", + "hdmi_input": "HDMI įvestis", + "hdmi_switcher": "HDMI perjungiklis", + "information": "Informacija", + "lights": "Šviesos", + "turn_tv_off": "Išjunkite televizorių" + }, + "names": { + "family_room": "Šeimos kambarys", + "hallway": "Prieškambaris", + "kitchen": "Virtuvė", + "master_bedroom": "Pagrindinis miegamasis", + "upstairs": "Viršuje" + }, + "unit": { + "minutes_abbr": "min" } } } }, "page-onboarding": { + "analytics": { + "finish": "Kitas" + }, + "core-config": { + "button_detect": "Aptikti", + "finish": "Kitas", + "location_name_default": "Namai" + }, + "finish": "Baigti", + "integration": { + "finish": "Baigti", + "more_integrations": "Daugiau" + }, + "next": "Kitas", "restore": { "description": "Taip pat galite atkurti iš ankstesnės momentinės kopijos.", "hide_log": "Slėpti visą žurnalą", @@ -1014,42 +2138,96 @@ "show_log": "Rodyti visą žurnalą" }, "user": { + "create_account": "Sukurti paskyrą", "data": { - "password_confirm": "Patvirtinti slaptažodį" + "name": "Vardas", + "password": "Slaptažodis", + "password_confirm": "Patvirtinti slaptažodį", + "username": "Vartotojo vardas" }, "error": { - "password_not_match": "Slaptažodžiai nesutampa" - } + "password_not_match": "Slaptažodžiai nesutampa", + "required_fields": "Užpildykite visus privalomus laukus" + }, + "intro": "Pradėkime nuo vartotojo paskyros sukūrimo.", + "required_field": "Būtina" } }, "profile": { + "advanced_mode": { + "description": "Atrakina išplėstines funkcijas.", + "title": "Išplėstinis režimas" + }, "change_password": { "current_password": "Dabartinis slaptažodis", "error_new_is_old": "Naujas slaptažodis turi skirtis nuo dabartinio slaptažodžio", "error_new_mismatch": "Nesutampa naujojo slaptažodžio laukai", + "error_required": "Privaloma", "header": "Keisti slaptažodį", "new_password": "Naujas slaptažodis", + "submit": "Pateikti", "success": "Slaptažodis pakeistas sėkmingai" }, + "current_user": "Šiuo metu esate prisijungęs kaip {fullName}.", + "dashboard": { + "header": "Prietaisų skydelis" + }, + "enable_shortcuts": { + "description": "Įjunkite arba išjunkite sparčiuosius klaviatūros klavišus, skirtus atlikti įvairius veiksmus vartotojo sąsajoje.", + "header": "Spartieji klaviatūros klavišai" + }, + "force_narrow": { + "header": "Visada slėpti šoninę juostą" + }, + "is_owner": "Jūs esate savininkas.", "language": { "dropdown_label": "Kalba", - "header": "Kalba" + "header": "Kalba", + "link_promo": "Padėkite versti" + }, + "logout": "Atsijungti", + "logout_text": "Ar tikrai norite atsijungti?", + "logout_title": "Atsijungti?", + "long_lived_access_tokens": { + "created": "Sukurta {date}" }, "mfa_setup": { "close": "Uždaryti", - "title_aborted": "Nutraukta" + "step_done": "{step} sąranka atlikta", + "submit": "Pateikti", + "title_aborted": "Nutraukta", + "title_success": "Sėkmė!" + }, + "mfa": { + "confirm_disable": "Ar jūs tikrai norite išjungti {name}?", + "disable": "Išjungti", + "enable": "Įgalinti" }, "number_format": { + "description": "Pasirinkite, kaip formatuojami skaičiai.", "dropdown_label": "Skaičių formatas", "formats": { + "comma_decimal": "1,234,567.89", + "decimal_comma": "1.234.567,89", "language": "Automatinis (naudoti kalbos nustatymą)", "none": "Joks", + "space_comma": "1 234 567,89", "system": "Naudoti sistemos lokalę" }, "header": "Skaičių formatas" }, "push_notifications": { - "description": "Siųsti pranešimus į šį įrenginį." + "add_device_prompt": { + "input_label": "Įrenginio pavadinimas" + }, + "description": "Siųsti pranešimus į šį įrenginį.", + "header": "\"Push\" pranešimai", + "link_promo": "Sužinokite daugiau", + "push_notifications": "\"Push\" pranešimai" + }, + "refresh_tokens": { + "created_at": "Sukurta {date}", + "last_used": "Paskutinį kartą naudota {date} iš {location}" }, "themes": { "accent_color": "Akcento spalva", @@ -1059,11 +2237,14 @@ "light": "Šviesi" }, "dropdown_label": "Tema", + "error_no_theme": "Temų nėra.", "header": "Tema", + "link_promo": "Sužinokite apie temas", "primary_color": "Pagrindinė spalva", "reset": "Atstatyti" }, "time_format": { + "description": "Pasirinkite, kaip formatuojami laikai.", "dropdown_label": "Laiko formatas", "formats": { "12": "12 valandų (AM/PM)", diff --git a/translations/frontend/ru.json b/translations/frontend/ru.json index b611bf919d..639463d622 100644 --- a/translations/frontend/ru.json +++ b/translations/frontend/ru.json @@ -2965,6 +2965,7 @@ "home_id": "ID дома", "network": "Сеть", "node_id": "ID узла", + "reconfigure_server": "Перенастроить сервер", "remove_node": "Удалить узел" }, "dashboard": { diff --git a/translations/frontend/zh-Hans.json b/translations/frontend/zh-Hans.json index a2d807aa69..3c0917b41d 100644 --- a/translations/frontend/zh-Hans.json +++ b/translations/frontend/zh-Hans.json @@ -2965,6 +2965,7 @@ "home_id": "家庭 ID", "network": "网络", "node_id": "节点 ID", + "reconfigure_server": "重新配置服务", "remove_node": "删除节点" }, "dashboard": { From 45436731e2843f216aeefaf555b49f6e6b600318 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Fri, 25 Jun 2021 06:36:23 +0200 Subject: [PATCH 21/38] Fix select entity disabled when no item selected (#9465) --- src/panels/lovelace/entity-rows/hui-select-entity-row.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/panels/lovelace/entity-rows/hui-select-entity-row.ts b/src/panels/lovelace/entity-rows/hui-select-entity-row.ts index 7133db16f8..ddf98be900 100644 --- a/src/panels/lovelace/entity-rows/hui-select-entity-row.ts +++ b/src/panels/lovelace/entity-rows/hui-select-entity-row.ts @@ -17,7 +17,7 @@ import { computeDomain } from "../../../common/entity/compute_domain"; import { computeStateName } from "../../../common/entity/compute_state_name"; import "../../../components/entity/state-badge"; import "../../../components/ha-paper-dropdown-menu"; -import { UNAVAILABLE_STATES } from "../../../data/entity"; +import { UNAVAILABLE } from "../../../data/entity"; import { forwardHaptic } from "../../../data/haptics"; import { SelectEntity, setSelectOption } from "../../../data/select"; import { ActionHandlerEvent } from "../../../data/lovelace"; @@ -87,7 +87,7 @@ class HuiSelectEntityRow extends LitElement implements LovelaceRow { > From 4ec9c9c16ea6fb2e13e73a23ab2ecef7bd4f656f Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Sat, 26 Jun 2021 00:46:57 +0000 Subject: [PATCH 22/38] Translation update --- translations/frontend/ca.json | 1 + translations/frontend/es.json | 2 +- translations/frontend/fr.json | 36 ++++++++++++++++++++++- translations/frontend/it.json | 1 + translations/frontend/ja.json | 47 ++++++++++++++++++++++++++++-- translations/frontend/nb.json | 1 + translations/frontend/nl.json | 1 + translations/frontend/ru.json | 10 +++---- translations/frontend/zh-Hant.json | 1 + 9 files changed, 90 insertions(+), 10 deletions(-) diff --git a/translations/frontend/ca.json b/translations/frontend/ca.json index c15932954c..46336463ad 100644 --- a/translations/frontend/ca.json +++ b/translations/frontend/ca.json @@ -2965,6 +2965,7 @@ "home_id": "ID principal", "network": "Xarxa", "node_id": "ID del node", + "reconfigure_server": "Torna a configurar el servidor", "remove_node": "Elimina node" }, "dashboard": { diff --git a/translations/frontend/es.json b/translations/frontend/es.json index 4ad16212b8..3446d51222 100644 --- a/translations/frontend/es.json +++ b/translations/frontend/es.json @@ -197,7 +197,7 @@ "backup": "copia de seguridad", "default": "predeterminado", "homeassistant": "homeassistant", - "manager": "gerente" + "manager": "Administrador" }, "stage": { "description": "Los complementos pueden tener una de tres etapas: \n\n {icon_stable} ** Estable **: estos son complementos listos para usarse en producción. \n\n {icon_experimental} ** Experimental **: estos pueden contener errores y pueden estar inacabados. \n\n {icon_deprecated} ** Obsoleto **: estos complementos ya no recibirán actualizaciones.", diff --git a/translations/frontend/fr.json b/translations/frontend/fr.json index 8955807461..b0d41ac16e 100644 --- a/translations/frontend/fr.json +++ b/translations/frontend/fr.json @@ -318,6 +318,14 @@ "no_addons": "Vous n'avez pas encore installé de modules complémentaires. Rendez-vous dans la boutique des modules complémentaires pour commencer!" }, "dialog": { + "hardware": { + "attributes": "Attributs", + "device_path": "Chemin du dispositif", + "id": "Identifiant", + "search": "Recherche de matériel", + "subsystem": "Sous-système", + "title": "Matériel informatique" + }, "network": { "connected_to": "Connecté à {ssid}", "dhcp": "DHCP", @@ -368,6 +376,8 @@ "error": "Une erreur inconnue s'est produite", "error_addon_no_ingress": "L’add-on demandé ne prend pas en charge l’entrée", "error_addon_not_found": "Module complémentaire introuvable", + "error_addon_not_installed": "Le module complémentaire demandé n'est pas installé. Veuillez l'installer d'abord", + "error_addon_not_started": "Le module complémentaire demandé n'est pas en cours d'exécution. Veuillez le lancer d'abord", "faq_link": "FAQ de My Home Assistant", "not_supported": "Cette redirection n'est pas prise en charge par votre instance Home Assistant. Vérifiez le {link} pour les redirections prises en charge et la version dans laquelle elles ont été introduites." }, @@ -386,8 +396,13 @@ "create_blocked_not_running": "La création d’un instantané n’est pas possible en ce moment car le système est en état {state}.", "create_snapshot": "Créer un instantané", "created": "créé", + "delete_selected": "Supprimer les instantanés sélectionnés", + "delete_snapshot_confirm": "Supprimer", + "delete_snapshot_text": "Voulez-vous supprimer {number} {number, plural,\n one {entité}\n other {entités}\n} ?", + "delete_snapshot_title": "Supprimer l'instantané", "description": "Les instantanés vous permettent de sauvegarder et de restaurer facilement toutes les données de votre instance Home Assistant.", "enter_password": "Veuillez entrer un mot de passe.", + "failed_to_delete": "Échec de la suppression du fichier: %@", "folder": { "addons/local": "Modules complémentaires locaux", "homeassistant": "Configuration Home Assistant", @@ -405,6 +420,8 @@ "password_protection": "Protection par mot de passe", "passwords_not_matching": "Les mots de passe ne correspondent pas", "security": "Sécurité", + "select_type": "Sélectionnez ce qu'il faut restaurer", + "selected": "{number} sélectionné", "type": "Type", "upload_snapshot": "Téléverser un instantané" }, @@ -722,6 +739,9 @@ "no_match": "Aucune pièce correspondante trouvée", "show_areas": "Afficher les pièces" }, + "attributes": { + "expansion_header": "Attributs" + }, "blueprint-picker": { "add_user": "Ajouter un utilisateur", "remove_user": "Supprimer l'utilisateur", @@ -924,6 +944,9 @@ "config_entry_system_options": { "enable_new_entities_description": "Si désactivé, les nouvelles entités découvertes pour {integration} ne seront pas ajoutées automatiquement à Home Assistant.", "enable_new_entities_label": "Activer les entités nouvellement ajoutées.", + "enable_polling_description": "Si Home Assistant doit interroger automatiquement les entités {integration} pour les mises à jour.", + "enable_polling_label": "Activez l'interrogation pour les mises à jour.", + "restart_home_assistant": "Vous devez redémarrer Home Assistant pour que vos modifications soient prises en compte.", "title": "Options système pour {integration}", "update": "Mise à jour" }, @@ -1722,6 +1745,7 @@ "title": "Alexa" }, "connected": "Connecté", + "connecting": "Connexion...", "connection_status": "État de la connexion cloud", "fetching_subscription": "Récupération de l'abonnement...", "google": { @@ -2065,7 +2089,8 @@ "scripts": "Scripts", "unknown_error": "Erreur inconnue", "unnamed_device": "Appareil sans nom", - "update": "Mettre à jour" + "update": "Mettre à jour", + "update_device_error": "Échec de la mise à jour de l’appareil" }, "entities": { "caption": "Entités", @@ -2198,6 +2223,7 @@ "depends_on_cloud": "Dépend du cloud", "device_unavailable": "Appareil non disponible", "devices": "{count} {count, plural,\n one {appareil}\n other {appareils}\n}", + "disable_error": "Échec de l’activation ou de la désactivation de l’intégration", "disable_restart_confirm": "Redémarrez Home Assistant pour terminer la désactivation de cette intégration", "disable": { "disable_confirm": "Voulez-vous vraiment désactiver cette entrée de configuration? Ses appareils et entités seront désactivés.", @@ -2209,6 +2235,7 @@ }, "disabled_cause": "Désactivé par {cause}" }, + "disabled_polling": "Francais", "documentation": "Documentation", "enable_restart_confirm": "Redémarrez Home Assistant pour terminer l'activation de cette intégration", "entities": "{count} {count, plural,\n one {entity}\n other {entities}\n}", @@ -2591,6 +2618,8 @@ "add_scene": "Ajouter une scène", "delete_confirm": "Êtes-vous sûr de vouloir supprimer cette scène?", "delete_scene": "Supprimer la scène", + "duplicate": "Dupliquer", + "duplicate_scene": "Dupliquée la scène", "edit_scene": "Éditer la scène", "header": "Éditeur de scène", "headers": { @@ -2919,6 +2948,8 @@ "follow_device_instructions": "Suivez les instructions fournies avec votre appareil pour déclencher l'appairage sur l'appareil.", "inclusion_failed": "Le nœud n'a pas pu être ajouté. Veuillez consulter les journaux pour plus d'informations.", "inclusion_finished": "Le nœud a été ajouté. Quelques minutes peuvent s'écouler avant que toutes les entités n'apparaissent, alors que nous terminons la mise en place du nœud en arrière-plan.", + "interview_failed": "L'interrogation de l'appareil a échoué. Des informations additionnelles peuvent être disponibles dans les journaux.", + "interview_started": "L'appareil est en cours d'interrogation. Cela peut prendre du temps.", "introduction": "Cet assistant vous guidera dans l'ajout d'un nœud à votre réseau Z-Wave.", "secure_inclusion_warning": "Les dispositifs sécurisés nécessitent une bande passante supplémentaire ; un trop grand nombre de dispositifs sécurisés peut ralentir votre réseau Z-Wave. Nous recommandons de n'utiliser l'inclusion sécurisée que pour les dispositifs qui en ont besoin, comme les serrures ou les ouvre-portes de garage.", "start_inclusion": "Commencer l'inclusion", @@ -2934,6 +2965,7 @@ "home_id": "ID de la maison", "network": "Réseau", "node_id": "ID du nœud", + "reconfigure_server": "Reconfigurer le serveur", "remove_node": "Supprimer le nœud" }, "dashboard": { @@ -2958,6 +2990,7 @@ }, "logs": { "log_level": "Niveau du journal", + "log_level_changed": "Niveau de journal modifié en : {level}", "subscribed_to_logs": "S'abonner aux messages du journal Z-Wave JS", "title": "Journaux Z-Wave JS" }, @@ -4022,6 +4055,7 @@ "formats": { "12": "12 heures (AM / PM)", "24": "24 heures", + "language": "Auto (utiliser le paramètre de langue)", "system": "Utiliser les paramètres régionaux du système" }, "header": "Format de l'heure" diff --git a/translations/frontend/it.json b/translations/frontend/it.json index ee8c41af03..d2ddb9ca09 100644 --- a/translations/frontend/it.json +++ b/translations/frontend/it.json @@ -2965,6 +2965,7 @@ "home_id": "ID Home", "network": "Rete", "node_id": "ID Nodo", + "reconfigure_server": "Riconfigurare il server", "remove_node": "Rimuovi Nodo" }, "dashboard": { diff --git a/translations/frontend/ja.json b/translations/frontend/ja.json index 839f80267b..aae44f3386 100644 --- a/translations/frontend/ja.json +++ b/translations/frontend/ja.json @@ -313,7 +313,12 @@ }, "dialog": { "hardware": { - "search": "ハードウェアの検索" + "attributes": "属性", + "device_path": "デバイスパス", + "id": "ID", + "search": "ハードウェアの検索", + "subsystem": "サブシステム", + "title": "ハードウェア" }, "network": { "connected_to": "{ssid}に接続しました", @@ -365,6 +370,7 @@ "error": "不明なエラーが発生しました", "error_addon_no_ingress": "要求されたアドオンは入力をサポートしていません", "error_addon_not_found": "アドオンが見つかりません", + "error_addon_not_installed": "要求されたアドオンがインストールされていません。先にインストールしてください。", "error_addon_not_started": "要求されたアドオンは実行されていません。最初に起動してください", "faq_link": "マイホームアシスタントFAQ", "not_supported": "このリダイレクトは、HomeAssistantインスタンスではサポートされていません。 サポートされているリダイレクトとそれらが導入されたバージョンについては、{link}を確認してください。" @@ -408,6 +414,7 @@ "password_protection": "パスワード保護", "passwords_not_matching": "パスワードが一致しません", "security": "セキュリティ", + "select_type": "復元する対象を選択する", "selected": "{number}が選択されました", "type": "タイプ", "upload_snapshot": "スナップショットのアップロード" @@ -568,9 +575,11 @@ }, "light": { "brightness": "明るさ", + "cold_white_value": "冷たい白い明るさ", "color_brightness": "色の明るさ", "color_temperature": "色温度", "effect": "効果", + "warm_white_value": "暖かい白い明るさ", "white_value": "白さの値" }, "lock": { @@ -890,6 +899,7 @@ } }, "service-control": { + "integration_doc": "統合ドキュメント", "required": "この項目は必須です", "service_data": "サービスデータ", "target": "ターゲット", @@ -918,6 +928,9 @@ "config_entry_system_options": { "enable_new_entities_description": "無効にするには、 {integration} から検出された新しいエンティティは Home Assistant に自動的に追加されません。", "enable_new_entities_label": "新しく追加されたエンティティを有効にします。", + "enable_polling_description": "ホームアシスタントが{integration}エンティティの更新情報を自動的にポーリングする場合。", + "enable_polling_label": "更新のポーリングを有効にします。", + "restart_home_assistant": "変更を有効にするには、HomeAssistantを再起動する必要があります。", "title": "{integration} のシステムオプション", "update": "更新" }, @@ -1197,13 +1210,20 @@ }, "zha_reconfigure_device": { "attribute": "属性", + "battery_device_warning": "再構成プロセスを開始する前に、バッテリ駆動デバイスをスリープ解除する必要があります。デバイスのスリープ解除方法については、デバイスのマニュアルを参照してください。", + "bind_header": "バインディング", "button_hide": "詳細を非表示", "button_show": "詳細を表示", + "cluster_header": "クラスター", "configuration_complete": "デバイスの再構成が完了しました。", "configuration_failed": "デバイスの再構成に失敗しました。追加情報はログで入手できる場合があります。", "configuring_alt": "設定", "heading": "デバイスを再設定", "in_progress": "デバイスを再構成中です。これには時間がかかる場合があります。", + "introduction": "Zigbee ネットワーク上のデバイスを再構成します。デバイスが正常に機能していない場合は、この機能を使用します。", + "min_max_change": "最小/最大/変更", + "reporting_header": "報告", + "run_in_background": "このダイアログを閉じても、再設定はバックグラウンドで行われます。", "start_reconfiguration": "再構成を開始" } }, @@ -1283,6 +1303,7 @@ "linked_entities_caption": "エンティティ", "name": "名前", "name_required": "名前は必須です", + "no_linked_entities": "このエリアにリンクしているエンティティはありません。", "unknown_error": "不明なエラー", "update": "更新" }, @@ -1738,6 +1759,7 @@ "remote": { "access_is_being_prepared": "リモート アクセスの準備中です。準備ができたらお知らせします。", "certificate_info": "証明書情報", + "connected": "接続", "info": "Home Assistant Cloud は、自宅の外からインスタンスへの安全なリモート接続を提供します。", "instance_is_available": "インスタンスは次の場所にあります", "instance_will_be_available": "インスタンスは次の時点で利用可能になります。", @@ -2177,10 +2199,13 @@ "caption": "インテグレーション", "config_entry": { "area": "{area}", + "check_the_logs": "ログの確認", + "configure": "設定", "delete": "削除", "delete_confirm": "この統合を削除しますか?", "device_unavailable": "デバイスを利用できません", "devices": "{count} {count, plural,\n one {デバイス}\n other {デバイス}\n}", + "disable_error": "統合の有効化または無効化に失敗しました", "disable_restart_confirm": "Homeassistantを再起動して、このintegrationを無効にします。", "disable": { "disable_confirm": "この設定項目を無効にしてよろしいですか?そのデバイスやエンティティは無効になります。", @@ -2192,6 +2217,7 @@ }, "disabled_cause": "{cause}によって無効化" }, + "disabled_polling": "更新されたデータの自動ポーリングが無効", "documentation": "ドキュメント", "enable_restart_confirm": "Home Assistantを再起動して、このintegrationを有効にします", "entities": "{count} {count, plural,\n one {エンティティ}\n other {エントリー}\n}", @@ -2203,6 +2229,7 @@ "no_area": "エリアなし", "not_loaded": "読み込まれていない場合は{logs_link} を確認してください", "options": "オプション", + "provided_by_custom_integration": "カスタムインテグレーションによる提供", "reload": "再読込", "reload_confirm": "インテグレーションが再読み込みされました", "reload_restart_confirm": "ホームアシスタントを再起動してこのインテグレーションの再読み込みを完了してください。\n", @@ -2210,7 +2237,9 @@ "restart_confirm": "ホーム アシスタントを再起動して、この統合の削除を完了します。", "services": "{count} {count, plural,\n one {サービス}\n other {サービス}\n}", "state": { + "failed_unload": "アンロードに失敗しました。", "loaded": "読み込み", + "migration_error": "移行エラー", "not_loaded": "読み込まれていません", "setup_retry": "設定をやり直す" }, @@ -2570,6 +2599,7 @@ "add_scene": "シーンを追加", "delete_confirm": "このシーンを削除してもよろしいですか?", "delete_scene": "シーンを削除", + "duplicate": "複製", "duplicate_scene": "シーンの複製", "edit_scene": "シーンを編集", "header": "シーンエディター", @@ -2804,9 +2834,12 @@ "value": "バリュー" }, "configuration_page": { + "shortcuts_title": "ショートカット", + "update_button": "構成の更新", "zha_options": { "default_light_transition": "デフォルトのライト遷移時間(秒)", - "enable_identify_on_join": "デバイスがネットワークに参加する際に、識別効果を有効にする" + "enable_identify_on_join": "デバイスがネットワークに参加する際に、識別効果を有効にする", + "title": "グローバルオプション" } }, "device_pairing_card": { @@ -2913,6 +2946,7 @@ "home_id": "ホームID", "network": "ネットワーク", "node_id": "ノードID", + "reconfigure_server": "サーバーの再設定", "remove_node": "ノードの削除" }, "dashboard": { @@ -2936,7 +2970,10 @@ "zwave_info": "Z-Wave情報" }, "logs": { - "log_level_changed": "ログレベルが変更されました。{level}に変更されました。" + "log_level": "ログレベル", + "log_level_changed": "ログレベルが変更されました。{level}に変更されました。", + "subscribed_to_logs": "Z-WaveJSログメッセージを購読しました...", + "title": "Z-WaveJSログ" }, "navigation": { "logs": "ログ", @@ -2954,6 +2991,9 @@ "header": "Z-Waveデバイスの設定", "introduction": "選択したデバイスのデバイス(ノード)固有の設定パラメータを管理および調整する", "parameter_is_read_only": "このパラメータは読み取り専用です。", + "set_param_accepted": "パラメータが更新されました。", + "set_param_error": "エラーが発生しました。", + "set_param_queued": "パラメーターの変更はキューに入れられており、デバイスがスリープ解除されると更新されます。", "zwave_js_device_database": "Z-Wave JSデバイスデータベース" }, "node_status": { @@ -2968,6 +3008,7 @@ "in_progress": "デバイスがインタビュー中です。これには時間がかかる場合があります。", "interview_complete": "デバイスのインタビューが完了しました。", "interview_failed": "デバイスのインタビューに失敗しました。追加情報がログに残っている場合があります。", + "introduction": "Z-Wave ネットワーク上のデバイスに再インタビューします。デバイスの機能が不足している場合や、機能が正しくない場合は、この機能を使用します。", "run_in_background": "このダイアログを閉じても、インタビューはバックグラウンドで継続されます。", "start_reinterview": "再インタビューを開始", "title": "Z-Waveデバイスへの再インタビュー" diff --git a/translations/frontend/nb.json b/translations/frontend/nb.json index ca09cccc72..cf43e1e937 100644 --- a/translations/frontend/nb.json +++ b/translations/frontend/nb.json @@ -2965,6 +2965,7 @@ "home_id": "Hjem-ID", "network": "Nettverk", "node_id": "Node-ID", + "reconfigure_server": "Konfigurer server på nytt", "remove_node": "Fjern node" }, "dashboard": { diff --git a/translations/frontend/nl.json b/translations/frontend/nl.json index 149728e21e..ca8aa52bd0 100644 --- a/translations/frontend/nl.json +++ b/translations/frontend/nl.json @@ -2965,6 +2965,7 @@ "home_id": "Home ID", "network": "Netwerk", "node_id": "Knooppunt-ID", + "reconfigure_server": "Herconfigureer server", "remove_node": "Knooppunt verwijderen" }, "dashboard": { diff --git a/translations/frontend/ru.json b/translations/frontend/ru.json index 639463d622..f710c5c02c 100644 --- a/translations/frontend/ru.json +++ b/translations/frontend/ru.json @@ -540,11 +540,11 @@ "climate": { "aux_heat": "Дополнительный нагрев", "away_mode": "Режим \"не дома\"", - "cooling": "{name} охлаждение", - "current_temperature": "{name} текущая температура", + "cooling": "Охлаждение {name}", + "current_temperature": "Текущая температура {name}", "currently": "Сейчас", "fan_mode": "Режим вентиляции", - "heating": "{name} обогрев", + "heating": "Обогрев {name}", "high": "высокий", "low": "низкий", "on_off": "Вкл / Выкл", @@ -553,8 +553,8 @@ "swing_mode": "Режим качания воздушных шторок", "target_humidity": "Заданная влажность", "target_temperature": "Заданная температура", - "target_temperature_entity": "{name} заданная температура", - "target_temperature_mode": "{name} заданная температура {mode}" + "target_temperature_entity": "Заданная температура {name}", + "target_temperature_mode": "Заданная температура {name} в режиме {mode}" }, "counter": { "actions": { diff --git a/translations/frontend/zh-Hant.json b/translations/frontend/zh-Hant.json index ff4f4f9cfb..cf81b2140b 100644 --- a/translations/frontend/zh-Hant.json +++ b/translations/frontend/zh-Hant.json @@ -2965,6 +2965,7 @@ "home_id": "家庭 ID", "network": "網路", "node_id": "節點 ID", + "reconfigure_server": "重新設定伺服器", "remove_node": "移除節點" }, "dashboard": { From 97312577829cf8c417324ef5479ca2ed40871dd4 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Sun, 27 Jun 2021 00:47:51 +0000 Subject: [PATCH 23/38] Translation update --- translations/frontend/de.json | 1 + translations/frontend/he.json | 3 ++- translations/frontend/ko.json | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/translations/frontend/de.json b/translations/frontend/de.json index 89fa98cd1d..620215d570 100644 --- a/translations/frontend/de.json +++ b/translations/frontend/de.json @@ -2965,6 +2965,7 @@ "home_id": "Heim-ID", "network": "Netzwerk", "node_id": "Knoten-ID", + "reconfigure_server": "Server neu konfigurieren", "remove_node": "Knoten entfernen" }, "dashboard": { diff --git a/translations/frontend/he.json b/translations/frontend/he.json index 995c0e25a3..d2a5d123bf 100644 --- a/translations/frontend/he.json +++ b/translations/frontend/he.json @@ -2003,7 +2003,7 @@ } }, "devices": { - "add_prompt": "אף {name} לא נוסף באמצעות מכשיר זה. אתה יכול להוסיף אחד על ידי לחיצה על כפתור + למעלה.", + "add_prompt": "אף {name} לא נוסף באמצעות התקן זה. ניתן להוסיף אחד על ידי לחיצה על כפתור + למעלה.", "automation": { "actions": { "caption": "כשמשהו מופעל...", @@ -2965,6 +2965,7 @@ "home_id": "מזהה בית", "network": "רשת", "node_id": "מזהה צומת", + "reconfigure_server": "קביעת תצורה מחדש של שרת", "remove_node": "הסר צומת" }, "dashboard": { diff --git a/translations/frontend/ko.json b/translations/frontend/ko.json index 819ce983e3..6df6cb3d6f 100644 --- a/translations/frontend/ko.json +++ b/translations/frontend/ko.json @@ -2965,6 +2965,7 @@ "home_id": "집 ID", "network": "네트워크", "node_id": "노드 ID", + "reconfigure_server": "서버 재구성", "remove_node": "노드 제거하기" }, "dashboard": { From 62228ef1445439d6d9746803ee16e23028277b18 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Mon, 28 Jun 2021 00:47:17 +0000 Subject: [PATCH 24/38] Translation update --- translations/frontend/es.json | 2 +- translations/frontend/hu.json | 245 ++++++++++++++++++++++++++++++++-- 2 files changed, 235 insertions(+), 12 deletions(-) diff --git a/translations/frontend/es.json b/translations/frontend/es.json index 3446d51222..cacfdbf8ea 100644 --- a/translations/frontend/es.json +++ b/translations/frontend/es.json @@ -197,7 +197,7 @@ "backup": "copia de seguridad", "default": "predeterminado", "homeassistant": "homeassistant", - "manager": "Administrador" + "manager": "administrador" }, "stage": { "description": "Los complementos pueden tener una de tres etapas: \n\n {icon_stable} ** Estable **: estos son complementos listos para usarse en producción. \n\n {icon_experimental} ** Experimental **: estos pueden contener errores y pueden estar inacabados. \n\n {icon_deprecated} ** Obsoleto **: estos complementos ya no recibirán actualizaciones.", diff --git a/translations/frontend/hu.json b/translations/frontend/hu.json index b4e93321d6..7d62d2a650 100644 --- a/translations/frontend/hu.json +++ b/translations/frontend/hu.json @@ -126,12 +126,14 @@ }, "dashboard": { "action_error": { + "go_to_config": "Nem sikerült elindítani a bővítményt - a konfiguráció ellenőrzése nem sikerült!", "install": "A bővítmény telepítése sikertelen", "restart": "A bővítmény újraindítása sikertelen", "start": "A bővítmény indítása sikertelen", "start_invalid_config": "Ugrás a konfigurációra", "stop": "A bővítmény leállítása sikertelen", - "uninstall": "A bővítmény törlése sikertelen" + "uninstall": "A bővítmény törlése sikertelen", + "validate_config": "Nem sikerült ellenőrizni a kiegészítő konfigurációját" }, "capability": { "apparmor": { @@ -141,13 +143,25 @@ "title": "Home Assistant Hitelesítés" }, "docker_api": { + "description": "A kiegészítő szerzője kérte a hozzáférését a rendszeren futó Docker-példányhoz. Ez a mód teljes hozzáférést és vezérlést biztosít a kiegészítőnek az egész Home Assistant rendszerhez, ami biztonsági kockázatokat jelent, és visszaélés esetén károsíthatja a rendszert. Ezért ez a szolgáltatás negatívan befolyásolja a Home assistant biztonságát\n\nEzt a hozzáférési szintet a program nem kapja meg automatikusan, ezt Önnek kell megerősítenie. Ehhez manuálisan le kell tiltania a kiegészítő védelmi módját. Csak akkor tiltsa le a védelmi módot, ha tudja, szükség van ÉS megbízik a kiegészítő forrásában.", "title": "Teljes Docker hozzáférés" }, + "full_access": { + "description": "Ez a kiegészítő teljes hozzáférést biztosít a rendszer hardveréhez, a kiegészítő szerzőének kérésére. A hozzáférés összehasonlítható a Docker privilegizált módjával. Mivel ez lehetséges biztonsági kockázatokat tár fel, ez a szolgáltatás negatívan befolyásolja a Home assistant biztonságát. \n\nEzt a hozzáférési szintet a program nem kapja meg automatikusan, ezt Önnek kell megerősítenie. Ehhez manuálisan le kell tiltania a kiegészítő védelmi módját. Csak akkor tiltsa le a védelmi módot, ha tudja, szükség van ÉS megbízik a kiegészítő forrásában.", + "title": "Teljes hozzáférés a hardverhez" + }, "hassio_api": { "title": "Supervisor API hozzáférés" }, + "homeassistant_api": { + "title": "Home Assistant API-hozzáférés" + }, "host_network": { - "description": "A bővítmények általában a saját elszigetelt hálózati rétegükben futnak, ami megakadályozza, hogy hozzáférjenek a gazdagép operációs rendszer hálózatához. Bizonyos esetekben ez a hálózati elkülönítés korlátozhatja a bővítményeket a szolgáltatásaik nyújtásában, ezért az elkülönítést a bővítmény szerzője feloldhatja, így a bővítmény teljes hozzáférést biztosít a gazdaszámítógép hálózati képességeihez. Ez több hálózati lehetőséget biztosít a bővítménynek, de csökkenti a biztonságot, ezért a bővítmény biztonsági besorolása csökken, ha ezt a beállítást a bővítmény használja." + "description": "A bővítmények általában a saját elszigetelt hálózati rétegükben futnak, ami megakadályozza, hogy hozzáférjenek a gazdagép operációs rendszer hálózatához. Bizonyos esetekben ez a hálózati elkülönítés korlátozhatja a bővítményeket a szolgáltatásaik nyújtásában, ezért az elkülönítést a bővítmény szerzője feloldhatja, így a bővítmény teljes hozzáférést biztosít a gazdaszámítógép hálózati képességeihez. Ez több hálózati lehetőséget biztosít a bővítménynek, de csökkenti a biztonságot, ezért a bővítmény biztonsági besorolása csökken, ha ezt a beállítást a bővítmény használja.", + "title": "Gazdahálózat" + }, + "host_pid": { + "description": "Általában a kiegészítő futtatásának folyamatait elkülönítik az összes többi rendszerfolyamattól. A kiegészítő szerzője azt kérte, hogy a kiegészítő hozzáférjen a gazda rendszerpéldányon futó rendszerfolyamatokhoz, és engedélyezze a kiegészítő számára a folyamatok futtatását a fogadó rendszeren is. Ez a mód teljes hozzáférést és vezérlést biztosít a kiegészítőnek az egész Home Assistant rendszerhez, ami biztonsági kockázatokat jelent, és visszaélés esetén károsíthatja a rendszert. Ezért ez a szolgáltatás negatívan befolyásolja a Home assistant biztonságát.\n\nEzt a hozzáférési szintet a program nem kapja meg automatikusan, ezt Önnek kell megerősítenie. Ehhez manuálisan le kell tiltania a kiegészítő védelmi módját. Csak akkor tiltsa le a védelmi módot, ha tudja, szükség van ÉS megbízik a kiegészítő forrásában." }, "label": { "apparmor": "apparmor", @@ -161,6 +175,9 @@ "rating": "értékelés", "stage": "szakasz" }, + "rating": { + "title": "Kiegészítő biztonsági besorolása" + }, "role": { "admin": "admin", "backup": "biztonsági mentés", @@ -176,6 +193,7 @@ "cpu_usage": "Bővítmény CPU-használat", "hostname": "Gazdagép neve", "install": "telepítés", + "new_update_available": "{name} {version} elérhető", "not_available_version": "A(z) {core_version_installed} otthoni segédet futtatja, hogy frissítse az Otthoni segéd legalább {core_version_needed} verzióját.", "open_web_ui": "Webes felhasználói felület megnyitása", "option": { @@ -198,7 +216,13 @@ "title": "Watchdog" } }, + "protection_mode": { + "content": "A kiegészítő védelmi módja le van tiltva! Ez a kiegészítő számára teljes hozzáférést biztosít a teljes rendszerhez, ami növeli a biztonsági kockázatokat, és helytelen használat esetén károsíthatja a rendszert. Csak akkor tiltsa le a védelmi módot, ha tudja, szükség van ÉS megbízik a kiegészítő forrásában.", + "enable": "Védelmi mód engedélyezése", + "title": "Figyelem: A védelmi mód ki van kapcsolva!" + }, "ram_usage": "Bővítmény RAM-használat", + "rebuild": "Újjraépít", "restart": "újraindítás", "start": "indítás", "stop": "leállítás", @@ -276,6 +300,14 @@ "no_addons": "Még nincs telepítve egyetlen bővítmény sem. A kezdéshez menjen át a bővítmény boltba!" }, "dialog": { + "hardware": { + "attributes": "Attribútumok", + "device_path": "Eszköz útvonala", + "id": "ID", + "search": "Hardver keresése", + "subsystem": "Alrendszer", + "title": "Hardver" + }, "network": { "connected_to": "Csatlakozva a következőhöz: {ssid}", "dhcp": "DHCP", @@ -293,7 +325,11 @@ "wpa": "wpa-psk" }, "registries": { + "add_new_registry": "Hozzáadás új rendszerleíró adatbázishoz", + "add_registry": "Hozzáadás a rendszerleíró adatbázishoz", + "failed_to_add": "Nem sikerült hozzáadni a rendszerleíró adatbázishoz", "password": "Jelszó", + "registry": "Regisztrálás", "remove": "Eltávolítás", "username": "Felhasználónév" }, @@ -315,6 +351,8 @@ "my": { "error": "Ismeretlen hiba történt", "error_addon_not_found": "A bővítmény nem található", + "error_addon_not_installed": "A kért kiegészítő nincs telepítve. Kérjük, először telepítse", + "error_addon_not_started": "A kért bővítmény nem fut. Kérjük, először indítsa el", "faq_link": "My Home Assistant GYIK" }, "panel": { @@ -326,13 +364,19 @@ "snapshot": { "addons": "Bővítmények", "available_snapshots": "Rendelkezésreálló pillanatképek", + "confirm_password": "Pillanatkép jelszó megerősítése", "could_not_create": "Nem lehet pillanatképet létrehozni", "create": "Létrehozás", "create_blocked_not_running": "Jelenleg nem lehet pillanatképet létrehozni, mivel a rendszer {state} állapotban van.", "create_snapshot": "Pillanatkép létrehozása", + "created": "Létrehozva", + "delete_selected": "A kijelölt pillanatképek törlése", "delete_snapshot_confirm": "törlés", + "delete_snapshot_text": "Törölni szeretné a {szám} {szám, többes szám,\n egy {pillanatkép}\n egyéb {pillanatképek}\n}?", + "delete_snapshot_title": "Pillanatkép törlése", "description": "A pillanatképekkel könnyen hozhatsz létre vagy tölthetsz vissza mentést a teljes Home Assistant példányodról.", "enter_password": "Add meg a jelszót", + "failed_to_delete": "Nem sikerült törölni", "folder": { "addons/local": "Helyi bővítmények", "homeassistant": "Home Assistant konfiguráció", @@ -348,7 +392,10 @@ "password": "Pillanatkép jelszava", "password_protected": "jelszóval védett", "password_protection": "Jelszóvédelem", + "passwords_not_matching": "A jelszavak nem egyeznek", "security": "Biztonság", + "select_type": "Válassza ki, hogy mit szeretne visszaállítani", + "selected": "{number} kiválasztva", "type": "Pillanatkép típusa", "upload_snapshot": "Pillanatkép feltöltése" }, @@ -384,7 +431,8 @@ "used_space": "Felhasznált terület" }, "log": { - "get_logs": "Nem sikerült lekérni a(z) {provider} naplókat, {error}" + "get_logs": "Nem sikerült lekérni a(z) {provider} naplókat, {error}", + "log_provider": "Naplószolgáltató" }, "supervisor": { "beta_backup": "A szolgáltatás aktiválása előtt győződj meg arról, hogy rendelkezel biztonsági másolattal az adatokról.", @@ -401,6 +449,7 @@ "leave_beta_description": "Stabil frissítések a Home Assistanthoz, a Supervisorhoz és a gazdagéphez", "ram_usage": "Supervisor RAM használat", "reload_supervisor": "Supervisor újratöltése", + "search": "Keresés", "share_diagnostics": "Diagnosztika megosztása", "share_diagnostics_description": "Összeomlási jelentések és diagnosztikai információk megosztása.", "share_diagonstics_title": "Segíts a Home Assistant fejlesztésében", @@ -412,6 +461,7 @@ }, "unhealthy_title": "A telepítés nem megfelelő", "unsupported_reason": { + "apparmor": "Az AppArmor nincs engedélyezve a gazdagépen", "container": "Konténerek, amelyekről ismert, hogy problémákat okoznak", "dbus": "DBUS", "docker_configuration": "Docker konfiguráció", @@ -496,8 +546,11 @@ }, "light": { "brightness": "Fényerő", + "cold_white_value": "Hideg fehér fény", + "color_brightness": "Színes fény", "color_temperature": "Színhőmérséklet", "effect": "Hatás", + "warm_white_value": "Meleg fehér fény", "white_value": "Fehér fényerő" }, "lock": { @@ -624,6 +677,9 @@ "addon-picker": { "addon": "Bővítmény", "error": { + "fetch_addons": { + "title": "Hiba történt a bővítmények beolvasása közben" + }, "no_supervisor": { "title": "Nincs Supervisor" } @@ -657,6 +713,9 @@ "today": "Ma" }, "data-table": { + "clear": "Tiszta", + "filtering_by": "Szűrés", + "hidden": "{number} rejtve", "no-data": "Nincs adat", "search": "Keresés" }, @@ -720,7 +779,9 @@ "was_unlocked": "fel lett oldva", "was_unplugged": "ki lett húzva", "was_unsafe": "nem volt biztonságos" - } + }, + "retrieval_error": "Hiba a naplóbejegyzés beolvasása során", + "show_trace": "Nyomkövetés megjelenítése" }, "media-browser": { "audio_not_supported": "A böngésződ nem támogatja az audio elemet.", @@ -769,6 +830,14 @@ "label": "Kép", "unsupported_format": "Nem támogatott formátum, válassz JPEG, PNG vagy GIF képet." }, + "related-filter-menu": { + "filter_by_area": "Szűrés terület szerint", + "filter_by_device": "Szűrés eszköz szerint", + "filter_by_entity": "Szűrés entitás szerint", + "filtered_by_area": "terület: {area_name}", + "filtered_by_device": "eszköz: {device_name}", + "filtered_by_entity": "entitás: {entity_name}" + }, "related-items": { "area": "Terület", "automation": "A következő automatizálások része", @@ -835,6 +904,8 @@ "config_entry_system_options": { "enable_new_entities_description": "Ha le van tiltva, akkor az újonnan felfedezett {integration} entitások nem lesznek automatikusan hozzáadva a Home Assistant-hoz.", "enable_new_entities_label": "Újonnan hozzáadott entitások engedélyezése.", + "enable_polling_label": "A frissítések lekérdezésének engedélyezése.", + "restart_home_assistant": "A módosítások életbe lépéséhez újra kell indítania a Home Assistant alkalmazást.", "title": "{integration} rendszerbeállításai", "update": "Frissítés" }, @@ -844,6 +915,7 @@ }, "entity_registry": { "control": "Ellenőrzés", + "customize_link": "entitás testreszabása", "dismiss": "Elvetés", "editor": { "advanced": "Haladó beállítások", @@ -1060,6 +1132,11 @@ "perform_action": "{action} szerver", "restart": "Újraindítás", "stop": "Leállítás" + }, + "types": { + "navigation": "Navigál", + "reload": "Újratöltés", + "server_control": "Szerver" } }, "filter_placeholder": "Entitásszűrő" @@ -1076,6 +1153,7 @@ "buttons": { "add": "Eszközök hozzáadása ezen az eszközön keresztül", "clusters": "Klaszterek Kezelése", + "device_children": "Aleszközök megtekintése", "reconfigure": "Eszköz újrakonfigurálása", "remove": "Eszköz eltávolítása", "view_in_visualization": "Megtekintés a Vizualizációban", @@ -1084,6 +1162,7 @@ "confirmations": { "remove": "Biztosan el szeretnéd távolítani az eszközt?" }, + "device_children": "Zigbee aleszköz", "device_signature": "Zigbee eszköz aláírása", "last_seen": "Utolsó jelentés", "manuf": "{manufacturer} által", @@ -1100,6 +1179,21 @@ "zha_device_card": { "device_name_placeholder": "Eszköznév módosítása" } + }, + "zha_reconfigure_device": { + "attribute": "Attribútum", + "bind_header": "Kötés", + "button_hide": "Részletek elrejtése", + "button_show": "Részletek megjelenítése", + "cluster_header": "Klaszter", + "configuration_complete": "Az eszköz újrakonfigurálása befejeződött.", + "configuration_failed": "Az eszköz újrakonfigurálása nem sikerült. További információk elérhetők a naplókban.", + "configuring_alt": "Konfigurálás", + "heading": "Eszköz újrakonfigurálása", + "in_progress": "Az eszköz újrakonfigurálása folyamatban van. Ez eltarthat egy ideig.", + "min_max_change": "min/max/változás", + "reporting_header": "Jelentés", + "start_reconfiguration": "Újrakonfigurálás indítása" } }, "duration": { @@ -1118,6 +1212,10 @@ "key_missing": "A (z) \" {key}\" szükséges kulcs hiányzik.", "key_not_expected": "A(z) \"{key}\" kulcs nem várt, vagy a vizuális szerkesztő nem támogatja.", "no_type_provided": "Nincs megadva típus." + }, + "supervisor": { + "reboot": "Próbálja újraindítani a gazdagépet", + "system_health": "Rendszerállapot ellenőrzése" } }, "login-form": { @@ -1135,10 +1233,12 @@ "notification_toast": { "connection_lost": "A kapcsolat megszakadt. Újracsatlakozás…", "dismiss": "Elvetés", + "integration_starting": "{integration} indítása folyamatban van, nem lesz elérhető, amíg ez be nem fejeződik.", "service_call_failed": "Nem sikerült meghívni a(z) {service} szolgáltatást.", "started": "A Home Assistant indítása befejeződött!", "starting": "A Home Assistant indítása folyamatban van. Nem lesz minden elérhető, amíg ez be nem fejeződik.", - "triggered": "Aktiválva {name}" + "triggered": "Aktiválva {name}", + "wrapping_up_startup": "Az indítás folyamatban. A folyamat befejezéséig nem minden lesz elérhető." }, "panel": { "config": { @@ -1150,7 +1250,8 @@ "caption": "Területek", "data_table": { "area": "Terület", - "devices": "Eszközök" + "devices": "Eszközök", + "entities": "Entitások" }, "delete": { "confirmation_text": "Minden ebben a területben lévő eszköz hozzárendelés nélküli lesz.", @@ -1227,6 +1328,7 @@ "extra_fields": { "brightness_pct": "Fényerő", "code": "Kód", + "flash": "Villogás", "humidity": "Páratartalom", "message": "Üzenet", "mode": "Üzemmód", @@ -1392,6 +1494,7 @@ "move_down": "Le", "move_up": "Fel", "save": "Mentés", + "show_trace": "Nyomkövetés megjelenítése", "triggers": { "add": "Eseményindító hozzáadása", "delete": "Törlés", @@ -1500,6 +1603,7 @@ "add_automation": "Automatizálás hozzáadása", "delete_automation": "Automatizálás törlése", "delete_confirm": "Biztosan törölni szeretnéd ezt az automatizálást?", + "dev_automation": "Hibakeresési automatizálás", "duplicate": "Duplikálás", "duplicate_automation": "Automatizálás duplikálása", "edit_automation": "Automatizálás szerkesztése", @@ -1599,6 +1703,7 @@ "info": "A Google Asszisztens integrációval a Home Assistant Felhő lehetőséget nyújt minden Home Assistant eszköz vezérlésére bármely Google Asszisztens képes eszközzel.", "info_state_reporting": "Ha engedélyezed az állapotjelentést, akkor a Home Assistant minden állapotváltozást el fog küldeni a feltárt entitásokról a Google-nak. Ez lehetővé teszi, hogy mindig láthasd a legfrissebb állapotokat a Google alkalmazásban.", "manage_entities": "Entitások kezelése", + "not_configured_title": "A Google Assistant nincs aktiválva", "security_devices": "Biztonsági eszközök", "sync_entities": "Entitások szinkronizálása a Google-lal", "sync_entities_404_message": "Nem sikerült szinkronizálni az entitásokat a Google-lal. A szinkronizáláshoz kérd meg a Google-t a következőre: \"Hey Google, sync my devices\"", @@ -1614,10 +1719,15 @@ "remote": { "access_is_being_prepared": "A távoli hozzáférés előkészítése folyamatban van. Értesíteni fogunk, ha készen áll.", "certificate_info": "Tanúsítvány infó", + "connected": "Csatlakoztatva", "info": "A Home Assistant Felhő biztonságos távoli kapcsolatot nyújt a példányodhoz, miközben távol vagy.", "instance_is_available": "A példányod itt elérhető:", "instance_will_be_available": "A példányod itt lesz elérhető:", "link_learn_how_it_works": "Tudd meg, hogyan működik", + "not_connected": "Nincs csatlakoztatva", + "remote_enabled": { + "caption": "Automatikus csatlakozás" + }, "title": "Távoli vezérlés" }, "sign_out": "Kijelentkezés", @@ -1631,6 +1741,9 @@ "target": "Cél", "target_browser": "Böngésző" }, + "female": "Nő", + "male": "Férfi", + "title": "Szövegről beszédre", "try": "Próbálja meg" }, "webhooks": { @@ -1757,6 +1870,30 @@ "description": "Egységrendszer, hely, időzóna és egyéb általános paraméterek", "section": { "core": { + "analytics": { + "header": "Analitika", + "instance_id": "Példányazonosító: {huuid}", + "preference": { + "base": { + "description": "Példányazonosító, verzió és telepítési típus.", + "title": "Alapvető elemzés" + }, + "diagnostics": { + "title": "Diagnosztika" + }, + "statistics": { + "title": "Felhasználási statisztikák" + }, + "usage_supervisor": { + "description": "Nevek, verziók és képességek.", + "title": "Használt integrációk és kiegészítők" + }, + "usage": { + "description": "Nevek és verzióinformációk.", + "title": "Használt integrációk" + } + } + }, "core_config": { "edit_requires_storage": "A szerkesztő le van tiltva, mert a konfiguráció a configuration.yaml fájlban van tárolva.", "elevation": "Magasság", @@ -1886,7 +2023,8 @@ "scripts": "Szkriptek", "unknown_error": "Ismeretlen hiba", "unnamed_device": "Névtelen eszköz", - "update": "Frissítés" + "update": "Frissítés", + "update_device_error": "A készülék frissítése sikertelen" }, "entities": { "caption": "Entitások", @@ -1943,6 +2081,9 @@ "filtering_by": "Szűrés", "show": "Megmutat" }, + "hassio": { + "button": "Konfigurál" + }, "header": "Home Assistant beállítása", "helpers": { "caption": "Segítők", @@ -1990,6 +2131,7 @@ "license": "Megjelent az Apache 2.0 licenc alatt", "path_configuration": "A configuration.yaml fájl elérési útja: {path}", "server": "server", + "setup_time": "Beállítási idő", "source": "Forrás:", "system_health_error": "A rendszerállapot összetevő nincs betöltve. Add hozzá a 'system_health:' sort a configuration.yaml fájlhoz.", "system_health": { @@ -2008,10 +2150,13 @@ "caption": "Integrációk", "config_entry": { "area": "Terület: {area}", + "check_the_logs": "Ellenőrizze a naplókat", + "configure": "Konfigurál", "delete": "Törlés", "delete_confirm": "Biztosan törölni szeretnéd ezt az integrációt?", "device_unavailable": "Eszköz nem érhető el", "devices": "{count} {count, plural,\n one {eszköz}\n other {eszköz}\n}", + "disable_error": "Az integráció engedélyezése vagy letiltása sikertelen volt", "disable_restart_confirm": "Indítsa újra a Home Assistant programot az integráció lekapcsolásának befejezéséhez", "disable": { "disable_confirm": "Biztosan letiltja ezt a konfigurációs bejegyzést? Eszközeit és entitásait letiltjuk.", @@ -2023,14 +2168,17 @@ }, "disabled_cause": "Letiltva ez által: {cause}" }, + "disabled_polling": "A frissített adatok automatikus lekérdezése le van tiltva", "documentation": "Dokumentáció", "enable_restart_confirm": "Indítsa újra a Home Assistant programot az integráció bekapcsolásának befejezéséhez", "entities": "{count} {count, plural,\n one {entitás}\n other {entitás}\n}", "entity_unavailable": "Entitás nem érhető el", "firmware": "Firmware: {version}", "hub": "Kapcsolódva", + "logs": "naplók", "manuf": "{manufacturer} által", "no_area": "Nincs Terület", + "not_loaded": "Nincs betöltve", "options": "Opciók", "reload": "Újratöltés", "reload_confirm": "Az integráció újra lett töltve", @@ -2038,6 +2186,11 @@ "rename": "Átnevezés", "restart_confirm": "Indítsd újra a Home Assistant-ot az integráció törlésének befejezéséhez", "services": "{count} {count, plural,\n one {szolgáltatás}\n other {szolgáltatás}\n}", + "state": { + "loaded": "Betöltve", + "not_loaded": "Nincs betöltve", + "setup_error": "Nem sikerült beállítani" + }, "system_options": "Rendszerbeállítások", "unnamed_entry": "Névtelen bejegyzés" }, @@ -2057,6 +2210,7 @@ "loading_first_time": "Kérlek várj, amíg az integráció telepítésre kerül", "next": "Következő", "not_all_required_fields": "Nincs kitöltve minden szükséges mező.", + "not_loaded": "Az integrációt nem sikerült betölteni. Próbálja meg újraindítani a Home Assistantot.", "pick_flow_step": { "title": "Felfedeztük ezeket, be akarod állítani őket?" }, @@ -2070,6 +2224,7 @@ "disable": { "disabled_integrations": "{number} letiltva", "hide_disabled": "A letiltott integrációk elrejtése", + "show": "Mutat", "show_disabled": "A letiltott integrációk megjelenítése" }, "discovered": "Felfedezett", @@ -2103,8 +2258,16 @@ "logs": { "caption": "Napló", "clear": "Törlés", + "custom_integration": "egyedi integráció", "description": "A Home Assistant naplófájlok megtekintése", "details": "Naplóbejegyzés részletei ({level})", + "level": { + "critical": "KRITIKUS HIBA", + "debug": "DEBUG", + "error": "HIBA", + "info": "INFÓ", + "warning": "FIGYELMEZTETÉS" + }, "load_full_log": "Teljes Home Assistant napló betöltése", "loading_log": "Hibanapló betöltése...", "multiple_messages": "az üzenet először a következő időpontban fordult elő: {time}, majd később {counter} alkalommal ismétlődött", @@ -2382,6 +2545,8 @@ "add_scene": "Jelenet hozzáadása", "delete_confirm": "Biztosan törölni szeretnéd ezt a jelenetet?", "delete_scene": "Jelenet törlése", + "duplicate": "Másolás", + "duplicate_scene": "Jelenet duplikálása", "edit_scene": "Jelenet szerkesztése", "header": "Jelenet szerkesztő", "headers": { @@ -2703,24 +2868,41 @@ }, "zwave_js": { "add_node": { + "inclusion_finished": "A csomópont hozzá lett adva.", + "title": "Adjon hozzá egy Z-Wave csomópontot", "view_device": "Eszköz megtekintése" }, "button": "Konfigurálja", "common": { "add_node": "Node hozzáadása", "close": "Bezárás", + "home_id": "Otthon azonosító", "network": "Hálózat", "node_id": "Node azonosító", + "reconfigure_server": "Kiszolgáló újrakonfigurálása", "remove_node": "Node eltávolítása" }, "dashboard": { + "driver_version": "Illesztőprogram verzió", "dump_not_ready_confirm": "Letöltés", + "header": "Kezelje Z-Wave hálózatát", + "home_id": "Otthon azonosító", + "nodes_ready": "Csomópontok készen állnak", "server_version": "Szerver verzió" }, "device_info": { - "device_config": "Eszköz beállítása" + "device_config": "Eszköz beállítása", + "node_ready": "Csomópont készen áll", + "node_status": "Csomópont állapota", + "zwave_info": "Z-Wave infó" + }, + "logs": { + "log_level": "Naplószint", + "log_level_changed": "A naplószint megváltozott erre: {level}", + "title": "Z-Wave JS naplók" }, "navigation": { + "logs": "Naplók", "network": "Hálózat" }, "network_status": { @@ -2732,10 +2914,21 @@ "error_device_not_found": "Az eszköz nem található", "header": "Z-Wave eszköz konfigurálása", "parameter_is_read_only": "Ez a paraméter csak olvasható", - "set_param_error": "Hiba történt." + "set_param_accepted": "A paraméter frissült.", + "set_param_error": "Hiba történt.", + "zwave_js_device_database": "Z-Wave JS eszközadatbázis" }, "node_status": { + "alive": "Él", + "asleep": "Alszik", + "awake": "Ébren", + "dead": "Halott", "unknown": "Ismeretlen" + }, + "remove_node": { + "cancel_exclusion": "A kizárás megszakítása", + "start_exclusion": "Kizárás indítása", + "title": "Z-Wave csomópont eltávolítása" } }, "zwave": { @@ -2942,7 +3135,11 @@ "cards": { "actions": { "action_confirmation": "Biztosan futtatod a(z) \"{action}\" műveletet?", - "no_service": "Nincs megadva futtatandó szolgáltatás" + "no_entity_more_info": "A további információkhoz nincs megadva entitás", + "no_entity_toggle": "A váltáshoz nincs megadva entitás", + "no_navigation_path": "Nincs megadva navigációs útvonal", + "no_service": "Nincs megadva futtatandó szolgáltatás", + "no_url": "Nincs megadva megnyitandó URL" }, "confirm_delete": "Biztosan törölni szeretnéd ezt a kártyát?", "empty_state": { @@ -3554,6 +3751,9 @@ } }, "page-onboarding": { + "analytics": { + "finish": "Következő" + }, "core-config": { "button_detect": "Észlelés", "finish": "Tovább", @@ -3563,17 +3763,28 @@ "location_name": "A Home Assistant rendszered neve", "location_name_default": "Otthon" }, + "finish": "Befejezés", "integration": { "finish": "Befejezés", "intro": "Az eszközöket és szolgáltatásokat a Home Assistant integrációként kezeli. Beállíthatod őket most, vagy később a konfigurációs képernyőn.", "more_integrations": "Több" }, "intro": "Készen állsz arra, hogy felébreszd az otthonod, visszaszerezd a magánéleted és csatlakozz egy világhálós közösséghez?", + "next": "Következő", "restore": { + "addons": "Kiegészítők", + "confirm_password": "Erősítse meg a Pillanatkép jelszavát", "description": "Alternatív megoldásként visszaállíthatsz egy korábbi pillanatképet.", + "folders": "Mappák", + "full_snapshot": "Teljes pillanatkép", "hide_log": "Teljes napló elrejtése", "in_progress": "Visszaállítás folyamatban", - "show_log": "Teljes napló megjelenítése" + "partial_snapshot": "Részleges pillanatkép", + "password": "Snapshot jelszó", + "password_protection": "Jelszóvédelem", + "select_type": "Válassza ki a visszaállítandó elemeket", + "show_log": "Teljes napló megjelenítése", + "type": "Pillanatkép típus" }, "user": { "create_account": "Fiók Létrehozása", @@ -3663,6 +3874,18 @@ "enable": "Engedélyez", "header": "Többfaktoros Hitelesítési Modulok" }, + "number_format": { + "dropdown_label": "Számformátum", + "formats": { + "comma_decimal": "1 234 567,89", + "decimal_comma": "1.234.567,89", + "language": "Automatikus (nyelvi beállítás használata)", + "none": "Nincs", + "space_comma": "1 234 567,89", + "system": "Használja a rendszer területi beállításait" + }, + "header": "Számformátum" + }, "push_notifications": { "add_device_prompt": { "input_label": "Eszköz neve", From 08de941c902d5c482b36bf4700ed38159ce7a86e Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Tue, 29 Jun 2021 00:47:16 +0000 Subject: [PATCH 25/38] Translation update --- translations/frontend/ja.json | 12 +++++++++++- translations/frontend/pl.json | 1 + 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/translations/frontend/ja.json b/translations/frontend/ja.json index aae44f3386..a1c7643e4e 100644 --- a/translations/frontend/ja.json +++ b/translations/frontend/ja.json @@ -862,6 +862,12 @@ "label": "画像", "unsupported_format": "サポートされていない形式です。JPEG、PNG、またはGIF画像を選択してください。" }, + "related-filter-menu": { + "filter_by_area": "エリアで絞り込む", + "filter_by_device": "デバイスで絞り込む", + "filtered_by_area": "エリア: {area_name}", + "filtered_by_device": "デバイス: {device_name}" + }, "related-items": { "area": "エリア", "automation": "次の自動化の一部", @@ -1164,7 +1170,8 @@ }, "types": { "navigation": "ナビゲート", - "reload": "リロード" + "reload": "リロード", + "server_control": "サーバー" } }, "filter_placeholder": "エンティティフィルター" @@ -2203,6 +2210,7 @@ "configure": "設定", "delete": "削除", "delete_confirm": "この統合を削除しますか?", + "depends_on_cloud": "クラウドに依存", "device_unavailable": "デバイスを利用できません", "devices": "{count} {count, plural,\n one {デバイス}\n other {デバイス}\n}", "disable_error": "統合の有効化または無効化に失敗しました", @@ -2241,6 +2249,7 @@ "loaded": "読み込み", "migration_error": "移行エラー", "not_loaded": "読み込まれていません", + "setup_error": "セットアップに失敗しました。", "setup_retry": "設定をやり直す" }, "system_options": "システムオプション", @@ -3152,6 +3161,7 @@ "column_parameter": "パラメータ", "description": "サービス開発ツールを使用すると、ホームアシスタントで利用可能なサービスを呼び出すことができます。", "fill_example_data": "データ記入例", + "no_template_ui_support": "UIはテンプレートをサポートしていませんが、YAMLエディタを使用することができます。", "title": "サービス", "ui_mode": "UIモードに移動します", "yaml_mode": "YAMLモードに移動します", diff --git a/translations/frontend/pl.json b/translations/frontend/pl.json index 56f387bf39..53a5147026 100644 --- a/translations/frontend/pl.json +++ b/translations/frontend/pl.json @@ -2965,6 +2965,7 @@ "home_id": "Identyfikator domu", "network": "Sieć", "node_id": "Identyfikator węzła", + "reconfigure_server": "Ponownie skonfiguruj serwer", "remove_node": "Usuń węzeł" }, "dashboard": { From 0a83a704f1b40fc512fd143c0072c5a5f3917480 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20S=C3=B8rensen?= Date: Tue, 29 Jun 2021 14:57:25 +0200 Subject: [PATCH 26/38] Ignore previous versions in add-on changelog (#9474) --- hassio/src/addon-view/info/hassio-addon-info.ts | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/hassio/src/addon-view/info/hassio-addon-info.ts b/hassio/src/addon-view/info/hassio-addon-info.ts index 0fe37230bb..42b6969dbc 100644 --- a/hassio/src/addon-view/info/hassio-addon-info.ts +++ b/hassio/src/addon-view/info/hassio-addon-info.ts @@ -892,10 +892,19 @@ class HassioAddonInfo extends LitElement { private async _openChangelog(): Promise { try { - const content = await fetchHassioAddonChangelog( - this.hass, - this.addon.slug - ); + let content = await fetchHassioAddonChangelog(this.hass, this.addon.slug); + if ( + content.includes(`# ${this.addon.version}`) && + content.includes(`# ${this.addon.version_latest}`) + ) { + const newcontent = content.split(`# ${this.addon.version}`)[0]; + if (newcontent.includes(`# ${this.addon.version_latest}`)) { + // Only change the content if the new version still exist + // if the changelog does not have the newests version on top + // this will not be true, and we don't modify the content + content = newcontent; + } + } showHassioMarkdownDialog(this, { title: this.supervisor.localize("addon.dashboard.changelog"), content, From 43503ba0858ebd06a66f3d4dd4f2875120278079 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Tue, 29 Jun 2021 14:57:39 +0200 Subject: [PATCH 27/38] Fix number entity row availability when state unknown (#9475) --- src/panels/lovelace/entity-rows/hui-number-entity-row.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/panels/lovelace/entity-rows/hui-number-entity-row.ts b/src/panels/lovelace/entity-rows/hui-number-entity-row.ts index 97360e0192..84fe752862 100644 --- a/src/panels/lovelace/entity-rows/hui-number-entity-row.ts +++ b/src/panels/lovelace/entity-rows/hui-number-entity-row.ts @@ -11,7 +11,7 @@ import { customElement, property, state } from "lit/decorators"; import { computeStateDisplay } from "../../../common/entity/compute_state_display"; import { computeRTLDirection } from "../../../common/util/compute_rtl"; import "../../../components/ha-slider"; -import { UNAVAILABLE_STATES } from "../../../data/entity"; +import { UNAVAILABLE } from "../../../data/entity"; import { setValue } from "../../../data/input_text"; import { HomeAssistant } from "../../../types"; import { hasConfigOrEntityChanged } from "../common/has-changed"; @@ -75,7 +75,7 @@ class HuiNumberEntityRow extends LitElement implements LovelaceRow { ? html`
Date: Tue, 29 Jun 2021 15:00:27 +0200 Subject: [PATCH 28/38] Fix integration card rename dialog logic (#9467) --- .../config/integrations/ha-integration-card.ts | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/panels/config/integrations/ha-integration-card.ts b/src/panels/config/integrations/ha-integration-card.ts index 85663de87d..6549558047 100644 --- a/src/panels/config/integrations/ha-integration-card.ts +++ b/src/panels/config/integrations/ha-integration-card.ts @@ -303,7 +303,7 @@ export class HaIntegrationCard extends LitElement { > - + ${this.hass.localize( "ui.panel.config.integrations.config_entry.rename" )} @@ -420,6 +420,15 @@ export class HaIntegrationCard extends LitElement { showOptionsFlowDialog(this, ev.target.closest("ha-card").configEntry); } + private _handleRename(ev: CustomEvent): void { + if (!shouldHandleRequestSelectedEvent(ev)) { + return; + } + this._editEntryName( + ((ev.target as HTMLElement).closest("ha-card") as any).configEntry + ); + } + private _handleReload(ev: CustomEvent): void { if (!shouldHandleRequestSelectedEvent(ev)) { return; @@ -578,8 +587,7 @@ export class HaIntegrationCard extends LitElement { }); } - private async _editEntryName(ev) { - const configEntry = ev.target.closest("ha-card").configEntry; + private async _editEntryName(configEntry: ConfigEntry) { const newName = await showPromptDialog(this, { title: this.hass.localize("ui.panel.config.integrations.rename_dialog"), defaultValue: configEntry.title, From 42606062675aefa310af0b60b0db01e4cf9bdbcc Mon Sep 17 00:00:00 2001 From: Philip Allgaier Date: Tue, 29 Jun 2021 15:01:18 +0200 Subject: [PATCH 29/38] Set minimum = 1 hours to show (#9466) --- .../lovelace/editor/config-elements/hui-graph-footer-editor.ts | 1 + .../lovelace/editor/config-elements/hui-logbook-card-editor.ts | 1 + .../lovelace/editor/config-elements/hui-sensor-card-editor.ts | 1 + 3 files changed, 3 insertions(+) diff --git a/src/panels/lovelace/editor/config-elements/hui-graph-footer-editor.ts b/src/panels/lovelace/editor/config-elements/hui-graph-footer-editor.ts index 65b591c164..88277bc588 100644 --- a/src/panels/lovelace/editor/config-elements/hui-graph-footer-editor.ts +++ b/src/panels/lovelace/editor/config-elements/hui-graph-footer-editor.ts @@ -81,6 +81,7 @@ export class HuiGraphFooterEditor "ui.panel.lovelace.editor.card.config.optional" )})" .value=${this._hours_to_show} + min="1" .configValue=${"hours_to_show"} @value-changed=${this._valueChanged} > diff --git a/src/panels/lovelace/editor/config-elements/hui-logbook-card-editor.ts b/src/panels/lovelace/editor/config-elements/hui-logbook-card-editor.ts index ae710c5dbf..83f99d944b 100644 --- a/src/panels/lovelace/editor/config-elements/hui-logbook-card-editor.ts +++ b/src/panels/lovelace/editor/config-elements/hui-logbook-card-editor.ts @@ -85,6 +85,7 @@ export class HuiLogbookCardEditor "ui.panel.lovelace.editor.card.config.optional" )})" .value=${this._hours_to_show} + min="1" .configValue=${"hours_to_show"} @value-changed=${this._valueChanged} > diff --git a/src/panels/lovelace/editor/config-elements/hui-sensor-card-editor.ts b/src/panels/lovelace/editor/config-elements/hui-sensor-card-editor.ts index 3f707cc1fe..c39f907677 100644 --- a/src/panels/lovelace/editor/config-elements/hui-sensor-card-editor.ts +++ b/src/panels/lovelace/editor/config-elements/hui-sensor-card-editor.ts @@ -177,6 +177,7 @@ export class HuiSensorCardEditor )})" type="number" .value=${this._hours_to_show} + min="1" .configValue=${"hours_to_show"} @value-changed=${this._valueChanged} > From ba0be927ed4d0e19dd40f58d3a27ca970b00aafe Mon Sep 17 00:00:00 2001 From: Philip Allgaier Date: Tue, 29 Jun 2021 15:01:42 +0200 Subject: [PATCH 30/38] Set min value for history graph card rendered hours (#9464) --- .../editor/config-elements/hui-history-graph-card-editor.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/panels/lovelace/editor/config-elements/hui-history-graph-card-editor.ts b/src/panels/lovelace/editor/config-elements/hui-history-graph-card-editor.ts index 6f07551a19..766fda9dd5 100644 --- a/src/panels/lovelace/editor/config-elements/hui-history-graph-card-editor.ts +++ b/src/panels/lovelace/editor/config-elements/hui-history-graph-card-editor.ts @@ -79,6 +79,7 @@ export class HuiHistoryGraphCardEditor "ui.panel.lovelace.editor.card.config.optional" )})" .value="${this._hours_to_show}" + min="1" .configValue=${"hours_to_show"} @value-changed="${this._valueChanged}" > From a3d1a3566ddefc2b2df4baf975a1f22f5c132760 Mon Sep 17 00:00:00 2001 From: uvjustin <46082645+uvjustin@users.noreply.github.com> Date: Tue, 29 Jun 2021 21:44:18 +0800 Subject: [PATCH 31/38] Fix ha-hls-player cleanup for lit 2 (#9388) Co-authored-by: Bram Kragten --- src/components/ha-hls-player.ts | 50 ++++++++++++++------------------- 1 file changed, 21 insertions(+), 29 deletions(-) diff --git a/src/components/ha-hls-player.ts b/src/components/ha-hls-player.ts index 6120e5aa91..17d3157884 100644 --- a/src/components/ha-hls-player.ts +++ b/src/components/ha-hls-player.ts @@ -7,7 +7,7 @@ import { PropertyValues, TemplateResult, } from "lit"; -import { customElement, property, query, state } from "lit/decorators"; +import { customElement, property, query } from "lit/decorators"; import { fireEvent } from "../common/dom/fire_event"; import { nextRender } from "../common/util/render-status"; import { getExternalConfig } from "../external_app/external_config"; @@ -42,27 +42,23 @@ class HaHLSPlayer extends LitElement { // don't cache this, as we remove it on disconnects @query("video") private _videoEl!: HTMLVideoElement; - @state() private _attached = false; - private _hlsPolyfillInstance?: HlsLite; - private _useExoPlayer = false; + private _exoPlayer = false; public connectedCallback() { super.connectedCallback(); - this._attached = true; + if (this.hasUpdated) { + this._startHls(); + } } public disconnectedCallback() { super.disconnectedCallback(); - this._attached = false; + this._cleanUp(); } protected render(): TemplateResult { - if (!this._attached) { - return html``; - } - return html`
- ${this.showAdvanced - ? html` -

- ${this.hass.localize( - "ui.panel.config.integrations.note_about_integrations" - )}
- ${this.hass.localize( - "ui.panel.config.integrations.note_about_website_reference" - )}${this.hass.localize( - "ui.panel.config.integrations.home_assistant_website" - )}. -

- ` - : ""} `; } @@ -193,9 +193,6 @@ class StepFlowPickHandler extends LitElement { div { max-height: calc(100vh - 134px); } - div.advanced { - max-height: calc(100vh - 250px); - } } paper-icon-item { cursor: pointer; diff --git a/src/translations/en.json b/src/translations/en.json index dfb95a0586..c5bc152a6a 100755 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -2121,7 +2121,7 @@ "confirm_new": "Do you want to set up {integration}?", "add_integration": "Add integration", "no_integrations": "Seems like you don't have any integrations configured yet. Click on the button below to add your first integration!", - "note_about_integrations": "Not all integrations can be configured via the UI yet.", + "note_about_integrations": "No integrations matched your search, the integration you want to set up might not be available to set up via the UI yet.", "note_about_website_reference": "More are available on the ", "home_assistant_website": "Home Assistant website", "configure": "Configure", From ed4052365cc5887467edddeec6be55159c277926 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20S=C3=B8rensen?= Date: Wed, 30 Jun 2021 01:09:18 +0200 Subject: [PATCH 37/38] Allow placeholders in config and option flows (#9314) --- src/dialogs/config-flow/show-dialog-config-flow.ts | 5 ++++- src/dialogs/config-flow/show-dialog-options-flow.ts | 5 +++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/dialogs/config-flow/show-dialog-config-flow.ts b/src/dialogs/config-flow/show-dialog-config-flow.ts index fc49d0bf1f..86cf77be2d 100644 --- a/src/dialogs/config-flow/show-dialog-config-flow.ts +++ b/src/dialogs/config-flow/show-dialog-config-flow.ts @@ -90,7 +90,10 @@ export const showConfigFlowDialog = ( }, renderShowFormStepFieldError(hass, step, error) { - return hass.localize(`component.${step.handler}.config.error.${error}`); + return hass.localize( + `component.${step.handler}.config.error.${error}`, + step.description_placeholders + ); }, renderExternalStepHeader(hass, step) { diff --git a/src/dialogs/config-flow/show-dialog-options-flow.ts b/src/dialogs/config-flow/show-dialog-options-flow.ts index 536ead8026..dc508ee30e 100644 --- a/src/dialogs/config-flow/show-dialog-options-flow.ts +++ b/src/dialogs/config-flow/show-dialog-options-flow.ts @@ -88,9 +88,10 @@ export const showOptionsFlowDialog = ( ); }, - renderShowFormStepFieldError(hass, _step, error) { + renderShowFormStepFieldError(hass, step, error) { return hass.localize( - `component.${configEntry.domain}.options.error.${error}` + `component.${configEntry.domain}.options.error.${error}`, + step.description_placeholders ); }, From 9594c8106e531314e0c5e04d27acbacb51dcdb69 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Wed, 30 Jun 2021 00:47:15 +0000 Subject: [PATCH 38/38] Translation update --- translations/frontend/en.json | 15 ++++++++++++++- translations/frontend/es.json | 13 +++++++++++++ translations/frontend/et.json | 13 +++++++++++++ translations/frontend/ru.json | 17 +++++++++++++++-- 4 files changed, 55 insertions(+), 3 deletions(-) diff --git a/translations/frontend/en.json b/translations/frontend/en.json index c105428f82..dda85e0b25 100644 --- a/translations/frontend/en.json +++ b/translations/frontend/en.json @@ -2319,7 +2319,7 @@ "none": "Nothing configured yet", "none_found": "No integrations found", "none_found_detail": "Adjust your search criteria.", - "note_about_integrations": "Not all integrations can be configured via the UI yet.", + "note_about_integrations": "No integrations matched your search, the integration you want to set up might not be available to set up via the UI yet.", "note_about_website_reference": "More are available on the ", "reconfigure": "Reconfigure", "rename_dialog": "Edit the name of this config entry", @@ -2962,6 +2962,7 @@ "common": { "add_node": "Add Node", "close": "Close", + "heal_network": "Heal Network", "home_id": "Home ID", "network": "Network", "node_id": "Node ID", @@ -2988,6 +2989,18 @@ "node_status": "Node Status", "zwave_info": "Z-Wave Info" }, + "heal_network": { + "healing_cancelled": "Network healing has been cancelled.", + "healing_complete": "Network healing is complete.", + "healing_failed": "Healing failed. Additional information may be available in the logs.", + "in_progress": "Network healing is in progress. This will take some time.", + "introduction": "Start a network heal on your Z-Wave network. A network heal will cause all devices to re-calculate their routes back to the controller and is recommended if you have recently moved devices or your controller.", + "run_in_background": "You can close this dialog and the network healing will continue in the background.", + "start_heal": "Start Healing", + "stop_heal": "Stop Healing", + "title": "Heal your Z-Wave Network", + "traffic_warning": "The healing process generates a large amount of traffic on the Z-Wave network. This may cause devices to respond slowly (or not at all) while the heal is in progress." + }, "logs": { "log_level": "Log Level", "log_level_changed": "Log Level changed to: {level}", diff --git a/translations/frontend/es.json b/translations/frontend/es.json index cacfdbf8ea..946be9169b 100644 --- a/translations/frontend/es.json +++ b/translations/frontend/es.json @@ -2962,6 +2962,7 @@ "common": { "add_node": "Añadir Nodo", "close": "Cerrar", + "heal_network": "Sanar red", "home_id": "ID de casa", "network": "Red", "node_id": "ID del Nodo", @@ -2988,6 +2989,18 @@ "node_status": "Estado del Nodo", "zwave_info": "Información de Z-Wave" }, + "heal_network": { + "healing_cancelled": "Se ha cancelado la curación en red.", + "healing_complete": "La curación de la red se ha completado.", + "healing_failed": "La curación falló. Puede haber información adicional disponible en los registros.", + "in_progress": "La curación de la red está en curso. Esto llevará algún tiempo.", + "introduction": "Inicia una cura de red en tu red Z-Wave. Una curación de la red hará que todos los dispositivos vuelvan a calcular sus rutas de regreso al controlador y se recomienda si has movido dispositivos o tu controlador recientemente.", + "run_in_background": "Puedes cerrar este cuadro de diálogo y la curación de la red continuará en segundo plano.", + "start_heal": "Iniciar la curación", + "stop_heal": "Detener la curación", + "title": "Sana tu red Z-Wave", + "traffic_warning": "El proceso de curación genera una gran cantidad de tráfico en la red Z-Wave. Esto puede hacer que los dispositivos respondan lentamente (o no respondan en absoluto) mientras la curación está en curso." + }, "logs": { "log_level": "Nivel de registro", "log_level_changed": "Nivel de registro cambiado a: {level}", diff --git a/translations/frontend/et.json b/translations/frontend/et.json index 2f47fa463a..f3e6c180cf 100644 --- a/translations/frontend/et.json +++ b/translations/frontend/et.json @@ -2962,6 +2962,7 @@ "common": { "add_node": "Lisa sõlm", "close": "Sulge", + "heal_network": "Paranda võrgustikku", "home_id": "Kodu ID", "network": "Võrk", "node_id": "Sõlme ID", @@ -2988,6 +2989,18 @@ "node_status": "Sõlme olek", "zwave_info": "Z-Wave teave" }, + "heal_network": { + "healing_cancelled": "Võrgu parandamine on tühistatud.", + "healing_complete": "Võrgu parandamine on lõpule viidud.", + "healing_failed": "Parandamine nurjus. Lisateave võib olla saadaval logides.", + "in_progress": "Võrgu parandamine on pooleli. See võtab aega.", + "introduction": "Alusta oma Z-Wave võrgu parandamist. Võrgu parandamine põhjustab kõigi seadmete marsruudi kontrollerisse tagasi arvutamise ja on soovitatav, kui oled hiljuti seadmeid või oma kontrollerit teisaldanud.", + "run_in_background": "Saad selle dialoogi sulgeda ja võrgu parandamine jätkub taustal.", + "start_heal": "Alusta parandamist", + "stop_heal": "Peata parandamine", + "title": "Paranda oma Z-Wave võrku", + "traffic_warning": "Parandamissprotsess tekitab Z-Wave võrgus palju liiklust. See võib põhjustada seadmete aeglast reageerimise (või üldse mitte) kuni parandamine on pooleli." + }, "logs": { "log_level": "Logimise tase", "log_level_changed": "Silumisteave on muudetud: {level}", diff --git a/translations/frontend/ru.json b/translations/frontend/ru.json index f710c5c02c..61e3e07537 100644 --- a/translations/frontend/ru.json +++ b/translations/frontend/ru.json @@ -2962,6 +2962,7 @@ "common": { "add_node": "Добавить узел", "close": "Закрыть", + "heal_network": "Перенастройка сети", "home_id": "ID дома", "network": "Сеть", "node_id": "ID узла", @@ -2988,6 +2989,18 @@ "node_status": "Статус узла", "zwave_info": "Информация о Z-Wave" }, + "heal_network": { + "healing_cancelled": "Перенастройка сети была отменена.", + "healing_complete": "Перенастройка сети завершена.", + "healing_failed": "Не удалось завершить перенастройку сети. Дополнительная информация может быть доступна в журналах.", + "in_progress": "Идет перенастройка сети. Это займет некоторое время.", + "introduction": "Перенастройка сети заставит все устройства пересчитать свои маршруты к контроллеру. Эта процедура рекомендуется, если Вы переместили в другое место устройства или контроллер.", + "run_in_background": "Вы можете закрыть это диалоговое окно, перенастройка сети продолжится в фоновом режиме.", + "start_heal": "Начать перенастройку", + "stop_heal": "Остановить перенастройку", + "title": "Перенастройка сети Z-Wave", + "traffic_warning": "Во время перенастройки генерируется большой объем трафика в сети Z-Wave. В связи с этим, устройства могут отвечать с задержками (или вообще не отвечать)." + }, "logs": { "log_level": "Уровень", "log_level_changed": "Уровень журнала изменен на: {level}", @@ -3114,8 +3127,8 @@ "add_node": "Добавить узел", "add_node_secure": "Добавить защищенный узел", "cancel_command": "Отменить команду", - "heal_network": "Исправить сеть", - "heal_node": "Исправить узел", + "heal_network": "Перенастройка сети", + "heal_node": "Перенастроить узел", "node_info": "Информация об узле", "print_node": "Показать узел", "refresh_entity": "Обновить объект",